最小生成树(kruskal算法)+prim算法
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出orz
输入格式
第一行包含两个整数N、M,表示该图共有N个结点和M条无向边。(N<=5000,M<=200000)
接下来M行每行包含三个整数Xi、Yi、Zi,表示有一条长度为Zi的无向边连接结点Xi、Yi
输出格式
输出包含一个数,即最小生成树的各边的长度之和;如果该图不连通则输出orz
输入输出样例
输入 #1
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
输出 #1
7
说明/提示
时空限制:1000ms,128M
数据规模:
对于20%的数据:N<=5,M<=20
对于40%的数据:N<=50,M<=2500
对于70%的数据:N<=500,M<=10000
对于100%的数据:N<=5000,M<=200000
#include<iostream> #include<algorithm> using namespace std; const int inf=200000; struct node{ int u,v,w; }; struct node e[inf]; int n,m,f[inf],sum=0,cnt=0; bool cmp(node t1,node t2) { return t1.w<t2.w; } int find(int x) { if(x!=f[x]) return f[x]=find(f[x]); else return x; } int merge(int x,int y) { int x1=find(x); int y1=find(y); if(x1!=y1) { f[y1]=x1; return 1; } return 0; } int main() { int t1,t2; cin>>n>>m; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++) { cin>>e[i].u>>e[i].v>>e[i].w; merge(e[i].u,e[i].v); } cnt=0; for(int i=1;i<=n;i++) { if(f[i]==i) cnt++; } if(cnt!=1) { cout<<cnt<<endl; cout<<"orz"<<endl; return 0; } for(int i=1;i<=m;i++) f[i]=i; cnt=0; sort(e+1,e+m,cmp); for(int i=1;i<=m;i++) { if(merge(e[i].u,e[i].v)) { cnt++; sum=sum+e[i].w; } if(cnt==n-1) { break; } } cout<<sum<<endl; return 0; }
从边开始找
first 给所有的边从小到大排序
找的边要满足这样的条件 边的两点属于不同的连通分量(使用并查集)
找到n-1条边就行了
这就是克鲁斯卡尔算法
#include<iostream> #include<algorithm> using namespace std; const int inf=20000; int e[inf][inf],dis[inf],book[inf],n,m,sum=0,cnt=0,f[inf]; int find(int x) { if(x!=f[x]) return f[x]=find(f[x]); else return x; } int merge(int x,int y) { int x1=find(x); int y1=find(y); if(x1!=y1) { f[y1]=x1; return 1; } return 0; } int main() { int a,b,c,flag; cin>>n>>m; for(int i=1;i<=n;i++) f[i]=i; fill(e[0],e[0]+inf*inf,inf); fill(dis,dis+inf,inf); fill(book,book+inf,0); book[1]=1; for(int i=1;i<=m;i++) { e[i][i]=0; } for(int i=1;i<=m;i++) { cin>>a>>b>>c; e[a][b]=c; e[b][a]=c; merge(a,b); } cnt=0; for(int i=1;i<=n;i++) { if(find(i)==i) cnt++; } if(cnt!=1) { cout<<cnt<<endl; cout<<"orz"<<endl; return 0; } for(int i=1;i<=n;i++) dis[i]=e[1][i]; cnt=1; while(cnt<n) { int minn=inf; for(int i=1;i<=n;i++) { if(book[i]==0&&dis[i]<minn) { minn=dis[i]; flag=i; } } sum=sum+dis[flag]; cnt++; book[flag]=1; for(int j=1;j<=n;j++) { if(book[j]==0&&dis[j]>e[flag][j]) { dis[j]=e[flag][j]; } } } cout<<sum<<endl; return 0; }
prim算法
将图中的点分成两部分 选入的与未选入的
从任意的一点出发,出发点加入选入的部分,选出连接选入的部分与未选入的部分的最小的边,并将其加入选入的部分
则连接最小边的点也加入选入的部分,重复n-1次完成
很多人说它很像dijkstra算法
我看这就是dijkstra算法
如果你够坚强够勇敢,你就能驾驭他们