最小生成树的概念 

   给定无向图G = (V, E),连接G中所有点,且边集是E的子集的树称为G的生成树,而权值和最小的生成树称为最小生成树,即MST。

   构造MST的方法有很多种。常用的有Kruskal算法和Prim算法,前者好写,时间复杂度为O(m),后者稍微难写,时间复杂度O(n*n)。(n为树的节点数,m为边数)。

   

Kruskal算法(摘自刘汝佳白书P199):

   算法的第一步是给所有边按照从小到大的顺序排序,然后从小到大考查所有边。考查到边(u, v)的时候有两种情况:

   情况1:u, v此时属于同一个连通分量中,则加入(u, v)会形成环,不能加入该边。

   情况2:u, v属于不同的连通分量。那么加入(u, v)一定是最优情况。为什么呢?用反证法证。如果不加这条边得到最优解T,则T+(u,v)一定有且只有一个环,而且环中至少有一条边(u', v')权值大于或等于(u, v)。删除该边后,得到新树T' = T + (u,v) - (u', v'),权值和T <= T,所以加入(u, v)不会比不加入差。

   算法中,判断是否属于同一个连通分量用并查集即可。

 1 //Kruskal算法求MST,返回MST的权值和,如果不联通返回-1
 2 //时间复杂度O(m)
 3 
 4 struct Pat{                 //表示边
 5     int s, e, w;            //s, e表示边的两个点,w为权值
 6 };
 7 
 8 int f[N];                   //并查集
 9 
10 int kruskal(int n, int m)       //n为点的数量,m为边的数量
11 {
12     sort (p, p+m, cmp);
13     for (int i = 0; i < n; ++ i)
14         f[i] = i;
15     
16     int cost = 0;
17     for (int i = 0; i < m; ++ i){
18         int t1 = find(p[i].s), t2 = find(p[i].e);
19         if (t1 != t2){
20             cost += p[i].w;
21             f[t1] = t2;
22         }
23     }
24 
25     int tmp;
26     for (int i = 0; i < n; ++ i){
27         if (!i) tmp = find(i);
28         else if (tmp != find(i)) return -1;
29     }
30     return cost;
31 }
View Code

相关文章: