关于最小生成树 Kruskal 和 Prim 的简述(图论)

模版题为【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条双向边,因为每次纳入点,但时间复杂度又是按边算的。
代码如下——

 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 }
Kruskal

 

2.Prim 普里姆算法

概述:先将1个点纳入联盟内,于是每次新纳入离联盟最近的点(看联盟内的点连到联盟外的点的边权),更新距离。

实现:邻接矩阵 邻接表+优先队列

时间复杂度:O(n2) & O(m log n)

【别人说邻接表的是O(m log n),还是O(m+n)(??),可我用的是邻接表但并不那么觉得......既然这个算法几乎用不到,我就不深究了。然后我之后发现求SP最短路的Dijkstra 迪杰斯特拉算法除了“更新距离”那里不一样,其他代码都是一模一样的!⊙o⊙ 所以优化后的内容可以参考我的另外一篇的博文了。就是这个:关于最短路径问题(图论)

应用:稠密图(Dense Graph, 由于主要看点数)

注意——由于用到了邻接表的last[]和a[].next,所以要建双向边。

代码如下——(无优化的)

 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 }
Prim 邻接表

 

还有一个模版题:【uva 1395】Slim Span(图论--最小生成树+结构体快速赋值 模版题)

posted @ 2016-11-02 09:57  konjac蒟蒻  阅读(343)  评论(0编辑  收藏  举报