图论:最小生成树
Kruskal算法核心是加边,先把所有边按照权值从小到大排序,然后在剩下的所有没有被选过的边中,找到最小的边,如果和已经选取的边构成回路则放弃,选取次小边,直到选取了n-1条边为止,这样所有点就都连通了。
每次从边集中选取的权值最小的边的两个顶点如果属于不同的树,就把他们合并(把这条边加入子图),反之则去看下一条边。
n个点e条边的情况下,时间复杂度O(e^2),并查集优化之后O(eloge),适用于求稀疏图的最小生成树。
int find(int x) { return p[x]==x?x:p[x]=find(p[x]); }
这是自带路径压缩的并查集
bool cmp(const int x,const int y) { return w[x]<w[y]; } for(int i=1;i<=n;i++) p[i]=i; for(int i=1;i<=e;i++) r[i]=i; sort(r+1,r+e+1,cmp);
第一个for循环是让每个点自己独自成为一个集合
第二个for循环和sort是为了让所有边按照权重排序
之后的内容就很显然了,完整的代码如下,程序读入了点数n和每两个点之间的边权关系,输出了最小生成树的结果。
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int maxn=105; 5 const int maxm=10005; 6 int n; 7 long long ans=0; 8 // 9 int e=0; 10 int u[maxm],v[maxm],w[maxm]; 11 int p[maxn],r[maxm]; 12 bool cmp(const int x,const int y) 13 { 14 return w[x]<w[y]; 15 } 16 int find(int x) 17 { 18 return p[x]==x?x:p[x]=find(p[x]); 19 } 20 void kruskal() 21 { 22 for(int i=1;i<=n;i++) 23 p[i]=i; 24 for(int i=1;i<=e;i++) 25 r[i]=i; 26 sort(r+1,r+e+1,cmp); 27 for(int i=1;i<=e;i++) 28 { 29 int m=r[i]; 30 int x=find(u[m]); 31 int y=find(v[m]); 32 if(x!=y) 33 { 34 ans+=w[m]; 35 p[x]=y; 36 } 37 } 38 } 39 int main() 40 { 41 cin>>n; 42 for(int i=1;i<=n;i++) 43 for(int j=1;j<=n;j++) 44 { 45 e++; 46 u[e]=i,v[e]=j; 47 cin>>w[e]; 48 } 49 kruskal(); 50 cout<<ans<<endl; 51 return 0; 52 }
接下来我们介绍Prim算法,规定一个点集和一个边集,初始状态下点集中包含任意一个点,边集为空
然后重复一下操作直到点集中包含所有的点
在边集中选取权值最小的一条边<u,v>,并且满足u在当前点集中而v在其补集中,如果有多条边满足这个条件就取任意一条
将点v加入点集,<u,v>加入边集
最后我们通过最终的边集和点集描述最小生成树
对于时间复杂度来说,我写的好像是最第二种。。先粘在这里跑路了。。
以后实现了相关细节再补充这部分内容,代码附下面。程序读入了n个点和m条边,输出了最小生成树的每一条边和边权之和。使用了邻接数组的数据结构。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=1005,maxm=1005; 6 const int INF = 0x7fffffff; 7 int n, m; 8 int tot; 9 struct point 10 { 11 bool vis; 12 int t, w; 13 }; 14 struct G 15 { 16 int cur; 17 point e[maxm]; 18 }g[maxn]; 19 void addedge(int a,int b,int c) 20 { 21 tot++; 22 point tmp; 23 tmp.t = b; 24 tmp.w = c; 25 g[a].cur++; 26 int t = g[a].cur; 27 g[a].e[t] = tmp; 28 } 29 // 30 int vis[maxn]; 31 int fa[maxn]; 32 int dis[maxn]; 33 int tot1; 34 int b1[maxn]; 35 int b2[maxn]; 36 int ans; 37 // 38 void prim() 39 { 40 memset(vis,0,sizeof(vis)); 41 int cnt=0; 42 int p=1; 43 for (int i=1;i<=n;i++) 44 fa[i] = p; 45 vis[p] = 1; 46 cnt++; 47 for(int i=1;i<=n;i++) 48 { 49 for (int tmp=1;tmp<=g[p].cur;tmp++) 50 { 51 if(g[p].e[tmp].t==i) 52 dis[i]=g[p].e[tmp].w; 53 } 54 if(dis[i]==0) 55 dis[i]=INF; 56 } 57 while(cnt!=n) 58 { 59 int l = INF; 60 int q; 61 for(int i=1;i<=n;i++) 62 { 63 if(l>dis[i]&&!vis[i]) 64 { 65 l=dis[i]; 66 q=i; 67 } 68 } 69 vis[q]=true; 70 p=q; 71 if(l!=INF) 72 { 73 tot1++; 74 b1[tot1]=min(p,fa[p]); 75 b2[tot1]=max(p,fa[p]); 76 } 77 cnt++; 78 ans+=l; 79 for(int i=1;i<=n;i++) 80 if(!vis[i]) 81 { 82 int t=INF; 83 for (int tmp=1;tmp<=g[p].cur;tmp++) 84 { 85 if(g[p].e[tmp].t==i) 86 t=g[p].e[tmp].w; 87 } 88 if(dis[i]>t) 89 { 90 dis[i]=t; 91 fa[i]=p; 92 } 93 } 94 } 95 } 96 int main() 97 { 98 cin>>n>>m; 99 for(int i=1;i<=m;i++) 100 { 101 int a,b,c; 102 cin>>a>>b>>c; 103 addedge(a,b,c); 104 addedge(b,a,c); 105 } 106 prim(); 107 cout<<tot1<<endl; 108 for(int i=1;i<=tot1;i++) 109 cout<<i<<":"<<b1[i]<<" "<<b2[i]<<endl; 110 cout << ans << endl; 111 return 0; 112 }