模版题为【poj 1287】Networking。
题意我就不说了,我就想简单讲一下Kruskal和Prim算法。卡Kruskal的题似乎几乎为0。(●-`o´-)ノ
假设有一个N个点的连通图,有M条边(不定向),求MST(Minimal Spanning Tree)最小生成树的值。
1.Kruskal 克鲁斯卡算法
概述:将边从小到大排序,依次将边两端的不在同一个联通分量/联盟的点分别加入一个个联盟内,将边也纳入,计入答案。最终N个点合并为一个联盟,也就是纳入联盟内的边达到N-1条就结束算法。
实现:并查集。
时间复杂度:O(m log m+m*k)≈O(m log m)
应用:稀疏图(Sparse Graph, 由于主要看边数)
注意——无须建2条双向边,因为每次纳入点,但时间复杂度又是按边算的。
代码如下——
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 using namespace std; 7 const int N=110,M=5010,D=10010; 8 9 //这是无向边的图,不用建2倍的便关于方向的边。 10 struct node 11 { 12 int x,y,d; 13 node() {}// 14 node(int x,int y,int d):x(x),y(y),d(d) {}// 15 //bool operator < (const node& now) const 16 //{ return d<now.d; } 17 }a[M]; 18 int fa[N]; 19 int n,m; 20 21 bool cmp(node x,node y) {return x.d<y.d;} 22 int mmin(int x,int y) {return x<y?x:y;} 23 int ffind(int x) 24 {//保持树的形态不变,顺便把遍历过的结点都改成树根的子节点,而不是之前子节点的子节点(这样要find要调用很多层) 25 if (fa[x]!=x) fa[x]=ffind(fa[x]); 26 return fa[x]; 27 } 28 int Kruskal() 29 { 30 int i,k; 31 int x,y,xx,yy; 32 int cnt=0,ans=0; 33 sort(a+1,a+1+m,cmp); 34 for (i=1;i<=n;i++) fa[i]=i; 35 for (i=1;i<=m;i++)//从最小的边开始选 36 { 37 x=a[i].x,xx=ffind(x); 38 y=a[i].y,yy=ffind(y); 39 if (xx!=yy) 40 { 41 cnt++,ans+=a[i].d; 42 fa[xx]=yy; 43 if (cnt==n-1) break;//边选够了 44 } 45 } 46 return ans; 47 } 48 int main() 49 { 50 while (scanf("%d",&n)!=EOF && n) 51 { 52 scanf("%d",&m); 53 int i,x,y,d; 54 for (i=1;i<=m;i++) 55 { 56 scanf("%d%d%d",&x,&y,&d); 57 a[i]=node(x,y,d);// 58 } 59 printf("%d\n",Kruskal()); 60 } 61 return 0; 62 }
2.Prim 普里姆算法
概述:先将1个点纳入联盟内,于是每次新纳入离联盟最近的点(看联盟内的点连到联盟外的点的边权),更新距离。
实现:邻接矩阵 或 邻接表+优先队列
时间复杂度:O(n2) & O(m log n)
【别人说邻接表的是O(m log n),还是O(m+n)(??),可我用的是邻接表但并不那么觉得......既然这个算法几乎用不到,我就不深究了。然后我之后发现求SP最短路的Dijkstra 迪杰斯特拉算法除了“更新距离”那里不一样,其他代码都是一模一样的!⊙o⊙ 所以优化后的内容可以参考我的另外一篇的博文了。就是这个:关于最短路径问题(图论)】
应用:稠密图(Dense Graph, 由于主要看点数)
注意——由于用到了邻接表的last[]和a[].next,所以要建双向边。
代码如下——(无优化的)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 8 const int N=55,M=3010,D=110; 9 int n,m,len; 10 struct edge 11 { 12 int x,y,d,next; 13 edge() {} 14 edge(int x,int y,int d):x(x),y(y),d(d) {} 15 }a[M]; 16 int dis[N],vis[N],last[N]; 17 18 int mmin(int x,int y) {return x<y?x:y;} 19 void ins(int x,int y,int d) 20 { 21 a[++len]=edge(x,y,d); 22 a[len].next=last[x],last[x]=len; 23 } 24 int Prim() 25 { 26 int i,j,k,ans=0; 27 memset(vis,0,sizeof(vis)); 28 memset(dis,63,sizeof(dis));//点到联盟的距离 29 dis[1]=0;//candidate 30 for (i=1;i<=n;i++)//每次选一个到联盟距离最小的vertices顶点 31 { 32 int p=0; 33 for (j=1;j<=n;j++) 34 if (!vis[j] && dis[j]<dis[p]) p=j; 35 ans+=dis[p]; 36 dis[p]=0,vis[p]=1; 37 for (k=last[p];k;k=a[k].next)//调整 38 { 39 int y=a[k].y; 40 dis[y]=mmin(dis[y],a[k].d); 41 } 42 } 43 return ans; 44 } 45 int main() 46 { 47 int i,x,y,d; 48 while (scanf("%d",&n)!=EOF && n) 49 { 50 scanf("%d",&m); 51 memset(last,0,sizeof(last)); 52 len=0; 53 for (i=1;i<=m;i++) 54 { 55 scanf("%d%d%d",&x,&y,&d); 56 ins(x,y,d),ins(y,x,d); 57 } 58 m*=2; 59 printf("%d\n",Prim()); 60 } 61 return 0; 62 }