基本概念:
树:如果一个无向连通图G中不存在回路,则称图G是一颗树。
生成树:无向连通图G的一个子图如果是一颗包含了G中所有顶点的树,则称它为图G的生成树。
注意:生成树是图G的极小连通子图,表示在若在图中任添加一条边都将形成一个回路,同样的,若任意去掉一条边都将使图不在连通。
如果在边中加上权值,那么权值最小的生成树即为最小生成树,权值最大的生成树为最大生成树。
最小生成树算法:
由生成树的定义不难知道,BFS或DFS不重复的遍历结果就是一个生成树。通常情况下,我们都是要求最小生成树的。求最小生成树的算法有三种:Kruskal算法,Boruvka算法,Prime算法。常用的是Kruskal算法和Prime算法。
Kruskal算法核心思想:作用与边,每次选取当前可用的最小权值的边。
Kruskal算法有两个注意的地方:① 当前可用,即若某边的权值最小,但它关联的两个顶点本省已经在同一个集合里了也是不可选的。
② 最小权值,这个是必须的,不多说。
Prime算法核心思想:作用与顶点,通过选择当前可用的最小权值的边把其他顶点加入到生成树当中。
算法过程:
1.将一个图的顶点分为两部分,一部分是最小生成树中的结点(A集合),另一部分是未处理的结点(B集合)。
2.首先选择一个结点,将这个结点加入A中,然后,对集合A中的顶点遍历,找出A中顶点关联的边权值最小的那个B中的结点(设为v),将此顶点从B中删除,加入集合A中。
3.重复步骤2,直到B集合中的结点为空,结束此过程。
4.A集合中的结点就是由Prime算法得到的最小生成树的结点,依照步骤2的结点连接这些顶点,得到的就是这个图的最小生成树。
代码:
1 int mat[N][N];
2 int lowcost[N];
3 int pre[N];
4 int n, m;
5 bool used[N];
6
7 int Prime(int s)
8 {
9 for(int i=1; i<=n; i++)
10 lowcost[i] = mat[s][i], pre[i]=s;
11 used[s] = true;
12 lowcost[s] = 0;
13 int mst=0, cnt=0;
14 for(int i=1; i<n; i++)
15 {
16 int tmp=INF, k;
17 for(int j=1; j<=n; j++)
18 if(!used[j] && lowcost[j]<tmp)
19 tmp = lowcost[k=j];
20 if(tmp==INF) break;
21 used[k] = true;
22 // 输出最小生成树中边
23 printf("%d %d %d\n", k, pre[k], lowcost[k]);
24 mst += tmp;
25 if(++cnt ==n-1) break;
26 for(int j=1; j<=n; j++)
27 if(!used[j] && mat[k][j] < lowcost[j])
28 lowcost[j] = mat[k][j], pre[j]=k;
29 }
30 return mst;
31 }
View Code
最大生成树:
从最小生成的求法中,我们知道,关键都是每次选取权值最小的,那么最大生成树算法只需每次在Kruskal和Prime中选取最大的即可。
还可以这样,记录边权的时候,每次乘一个-1,变为负权的图,然后用Prime求最小生成树,再把结果×-1,也可得到最大生成树的权值。
次小生成树算法:
边集不完全相同的其它生成树中值最小的那个。因此在数值上,最小生成树可能会等于次小生成树,这种情况也可以看做最小生成数。
思路:最直观的解法是,首先求出最小生成树,并且记录最小生成树中的边,然后再枚举删除最小生成树的边并同时求最小生成树。
以POJ1679为例,Kruskal求次小生成树代码如下:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define _Clr(x, y) memset(x, y, sizeof(x))
5 #define INF 0x3f3f3f3f
6 #define N 110
7 using namespace std;
8
9 struct Edge
10 {
11 int a, b;
12 int c;
13 bool operator < (const Edge &a) const
14 {
15 return c < a.c;
16 }
17 }edge[N*N];
18 int bleg[N], n, m;
19 int mst_edge[N];
20
21 int find(int x)
22 {
23 int y = x;
24 while(y != bleg[y])
25 y = bleg[y];
26 while(x != bleg[x])
27 {
28 int px = bleg[x];
29 bleg[x] = y;
30 x = px;
31 }
32 return y;
33 }
34
35 void Union(int a, int b)
36 {
37 int pa=find(a), pb=find(b);
38 if(pa != pb)
39 bleg[pa] = pb;
40 }
41
42 inline void Init()
43 {
44 for(int i=0; i<=n; i++)
45 bleg[i] = i;
46 }
47
48 void Kruskal()
49 {
50 int mst_1=0;
51 Init();
52 _Clr(mst_edge, 0);
53 sort(edge, edge+m);
54 int k=0;
55 for(int i=0; i<m && k<n-1; i++)
56 {
57 int a=find(edge[i].a);
58 int b=find(edge[i].b);
59 if(a != b)
60 {
61 mst_edge[k] = i;
62 Union(a, b);
63 mst_1 += edge[i].c;
64 k++;
65 }
66 }
67 // 枚举删除最小生成树中所有边求MST
68 for(int i=0; i<n-1; i++)
69 {
70 int mst_2=0, k=0;
71 Init();
72 for(int j=0; j<m && k<n-1; j++)
73 {
74 if(mst_edge[i] == j) continue;
75 int a=find(edge[j].a);
76 int b=find(edge[j].b);
77 if(a != b)
78 {
79 Union(a, b);
80 mst_2 += edge[j].c;
81 k++;
82 }
83 }
84 if(mst_1==mst_2 && k==n-1)
85 {
86 puts("Not Unique!");
87 return;
88 }
89 }
90 printf("%d\n", mst_1);
91 }
92
93 int main()
94 {
95 int T;
96 scanf("%d", &T);
97 while(T--)
98 {
99 scanf("%d%d", &n, &m);
100 for(int i=0; i<m; i++)
101 scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].c);
102 Kruskal();
103 }
104 return 0;
105 }
View Code