版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
kuangbin带你飞:点击进入新世界
最小生成算法模板:点击进入新世界
总结:
本人算是初学者中的初学者,欢迎交流~
感觉这个专题的难度比最短路还简单…可能是错觉吧…都是入门题,加深对算法的理解。
kuangbin的题型:
A.模板题 参考1.2.4.9.10
B.考察并查集 参考3.6.8
C.次小生成树 参考5
D. k条边免费(题型有点像最短路的分层图)参考7
文章目录
1.Jungle Roads
原题链接:传送门
思路:
- 模板题,把字符转化成整型后基本跟模板一模一样。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
int n,m,u,v,w,k;
char c,cc;
int f[manx];
struct node{
int u,v,w;
}a[manx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
while(cin>>n&&n)
{
k=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<n;i++)
{
cin>>c>>m;
for(int i=1;i<=m;i++)
{
cin>>cc>>w;
a[++k].u=c-'A'+1;
a[k].v=cc-'A'+1;
a[k].w=w;
}
}
sort(a+1,a+1+k,cmp);
ll ans=0;
int total=1;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
ans+=a[i].w;
f[u]=v;
total++;
if(total==n) break;
}
cout<<ans<<endl;
}
return 0;
}
2.Networking
原题链接:传送门
思路:
- 完完全全的模板题,按照最小生成树的思想写代码即可。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
while(cin>>n&&n)
{
k=0;
for(int i=1;i<=n;i++) f[i]=i;
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
a[++k].u=u;
a[k].v=v;
a[k].w=w;
}
sort(a+1,a+1+k,cmp);
ll ans=0;
int total=1;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
ans+=a[i].w;
f[u]=v;
total++;
if(total==n) break;
}
cout<<ans<<endl;
}
return 0;
}
3.Constructing Roads
原题链接:传送门
思路:
扫描二维码关注公众号,回复:
7629630 查看本文章
- 由于双向路,只需要记录一半的边就可以了。
- 常规最小生成树,只不过多了将部分点连接起来,可以用并查集完成这一份操作。
- 即便连接起来点原本的祖先是自己,将点连通的时候也需要先find(u),find(v),这一点可参考并查集的连通操作。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
while(cin>>n&&n)
{
k=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++){
cin>>w;
if(j>i)
a[++k].u=i,a[k].v=j,a[k].w=w;
}
}
cin>>m;
ll ans=0;
int total=1;
for(int i=1;i<=m;i++){
cin>>u>>v;
u=find(u),v=find(v);
if(u!=v){
f[u]=v;
total++;
}
}
sort(a+1,a+1+k,cmp);
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
ans+=a[i].w;
f[u]=v;
total++;
if(total==n) break;
}
cout<<ans<<endl;
}
return 0;
}
4.Agri-Net
原题链接:传送门
思路:
- 超级模板题,由于双向路,只需要记录一半的边就可以了。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define R register int
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
while(cin>>n&&n)
{
k=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++){
cin>>w;
if(j>i)
a[++k].u=i,a[k].v=j,a[k].w=w;
}
}
sort(a+1,a+1+k,cmp);
ll ans=0;
int total=1;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
ans+=a[i].w;
f[u]=v;
total++;
if(total==n) break;
}
cout<<ans<<endl;
}
return 0;
}
5.The Unique MST
原题链接:传送门
思路:
- 次小生成树的模板题,详细可以看开头的模板,里面有详细的解释,如果有不明白的地方可以私信交流~
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=200+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx],d[manx][manx];
vector<int>p[manx];
struct node{
int u,v,w;
bool vis;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
int t;
cin>>t;
while(t--)
{
k=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
p[i].clear();
p[i].push_back(i);
f[i]=i;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
a[++k].u=u,a[k].v=v,a[k].w=w,a[i].vis=false;
}
sort(a+1,a+1+k,cmp);
ll ans=0;
int total=1;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
ans+=a[i].w;
f[u]=v;
a[i].vis=1;
total++;
int l1=p[u].size(),l2=p[v].size();
for(int j=0;j<l1;j++)
for(int k=0;k<l2;k++)
d[p[u][j]][p[v][k]]=d[p[v][k]][p[u][j]]=a[i].w;
for(int j=0;j<l1;j++) p[v].push_back(p[u][j]);
if(total==n) break;
}
ll res=inf;
for(int i=1;i<=k;i++)
if(!a[i].vis)
res=min(res,ans+a[i].w-d[a[i].u][a[i].v]);
if(res>ans) cout<<ans<<endl;
else cout<<"Not Unique!"<<endl;
}
return 0;
}
6.Highways
原题链接:传送门
思路:
- 算是考察了并查集的连通操作,这道题的话因为是稠密图,用krus算法的话会超时,所以需要一些特别的优化技巧(剪枝技巧~ 详细见代码
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e4;
const int mamx=5e6+5000;
int n,m,u,v,w,k;
int x[manx],y[manx],f[manx];
struct node{
int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
scanf("%d",&n);
k=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
f[i]=i;
}
scanf("%d",&m);
int total=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
u=find(u),v=find(v);
if(u!=v){
f[u]=v;
total++; //连通时直接计算最小生成树集合里面的点数
}
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
u=find(i),v=find(j);
if(u==v) continue; //当边在最小生成树的集合里面跳过
w=(x[j]-x[i])*(x[j]-x[i])+(y[j]-y[i])*(y[j]-y[i]);
a[++k].u=i,a[k].v=j,a[k].w=w;
}
sort(a+1,a+1+k,cmp);
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
f[u]=v;
total++;
cout<<a[i].u<<" "<<a[i].v<<endl;
if(total==n) break;
}
return 0;
}
7.Arctic Network
原题链接:传送门
思路:
- 这道题巨坑,卡了很久,原因在G++不支持%lf,如果G++提交请用%f,如果C++提交请用%lf 。
- 可选择k条边免费,即求最小生成树第n-k大边,因为kru算法的边是排序好的,因此只要把第n-k条加入最小生成树的边输出就好。
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e3;
const int mamx=5e6+5000;
int n,m,u,v,w,k;
int x[manx],y[manx],f[manx];
struct node{
int u,v;
double w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
int t;
cin>>t;
while(t--){
scanf("%d%d",&n,&m);
k=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&x[i],&y[i]);
f[i]=i;
}
for(int i=1;i<=m;i++)
for(int j=i+1;j<=m;j++)
{
double z=sqrt((x[i]-x[j])*(x[i]-x[j])*1.0+(y[i]-y[j])*(y[i]-y[j])*1.0);
a[++k].u=i,a[k].v=j,a[k].w=z;
}
sort(a+1,a+1+k,cmp);
int total=1;
double ans=0.00;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
f[u]=v;
if(total==m-n){
ans=a[i].w;
break;
}
total++;
}
printf("%.2f\n",ans);
}
return 0;
}
8.Building a Space Station
原题链接:传送门
思路:
- 这道题同样需要注意:在G++不支持%lf,如果G++提交请用%f,如果C++提交请用%lf 。
- 在求两球之间的距离时,如果w<=0,则把这两个球之间的边加入最小生成树的集合中,其他没有什么坑点。
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e2+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
double x[manx],y[manx],z[manx],r[manx];
int f[manx];
struct node{
int u,v;
double w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
while(cin>>n&&n){
k=0;
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf%lf",&x[i],&y[i],&z[i],&r[i]);
f[i]=i;
}
int total=1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double w=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])+(z[i]-z[j])*(z[i]-z[j]))-r[i]-r[j];
if(w>0)a[++k].u=i,a[k].v=j,a[k].w=w;
else{
u=find(i),v=find(j);
if(u==v) continue;
f[u]=v;
total++;
}
}
sort(a+1,a+1+k,cmp);
double ans=0.000;
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
f[u]=v;
ans+=a[i].w;
total++;
if(total==n) break;
}
printf("%.3lf\n",ans);
}
return 0;
}
9.还是畅通工程
原题链接:传送门
思路:
- 模板题。但是输入貌似很多,需要用scanf,不能用cin,不然会超时。
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e2+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int f[manx];
struct node{
int u,v,w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
while(cin>>n&&n){
k=0;
m=n*(n-1)/2;
for(int i=1;i<=m;i++)
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
for(int i=1;i<=n;i++) f[i]=i;
int total=1;
ll ans=0;
sort(a+1,a+1+m,cmp);
for(int i=1;i<=m;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
f[u]=v;
ans+=a[i].w;
total++;
if(total==n) break;
}
printf("%d\n",ans);
}
return 0;
}
10.畅通工程再续
原题链接:传送门
思路:
- 模板题。
- 比普通的模板题多了x和y,需要把坐标化成距离,还有浮点的数据需要注意。
代码如下:
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cmath>
#define ll long long
#define R register int
#define inf 0x3f3f3f3f
using namespace std;
const int manx=1e2+5;
const int mamx=1e5+5;
int n,m,u,v,w,k;
int x[manx],y[manx];
int f[manx];
struct node{
int u,v;
double w;
}a[mamx];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x) return x;
else return f[x]=find(f[x]);
}
int main()
{
int t;
cin>>t;
while(t--){
scanf("%d",&n);
k=0;
for(int i=1;i<=n;i++)
scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double ww=sqrt((x[i]-x[j])*(x[i]-x[j])*1.000+(y[i]-y[j])*(y[i]-y[j]));
if(ww>1000||ww<10) continue;
a[++k].u=i,a[k].v=j,a[k].w=ww*100;
}
int total=1;
double ans=0;
sort(a+1,a+1+k,cmp);
for(int i=1;i<=k;i++)
{
u=find(a[i].u),v=find(a[i].v);
if(u==v) continue;
f[u]=v;
ans+=a[i].w;
total++;
}
if(total==n) printf("%.1lf\n",ans);
else puts("oh!");
}
return 0;
}