kruskal算法适用于求稀疏图的最小生成树,其基本思想基于贪心,核心操作为并查集的查询和合并操作。
思路为先对所有的权边进行排序,可以使用algorithm所带有的sort函数,然后枚举每一条边,如果入点和出点没有在同一个集合(并查集的查询操作)中那么就将这两个加入到同一个集合中(并查集的合并操作)。当每一条边都被枚举之后所求的就是最小生成树。
我们可以简单地证明一下该算法,当两个点没有在同一个集合中时说明关于可以合并这两个集合的所以边都没有被枚举到,由于我们对权边进行了排序,那么我们第一次所遇到的一定是关于这两个集合的最小权边,而当我们遇到了第二个可以连接这两个集合的边时,由于我们这两个集合已经被合并到了一起所以我们不会再将这条边加入生成树中。证毕。
模板:
1 #include <iostream>
2 #include <cstring>
3 #include <cstdio>
4 #include <algorithm>
5
6 using namespace std;
7
8 const int N = 5005, M = 2e5 + 10;
9 struct it
10 {
11 int a, b, w;
12 }edges[M];
13 int n, m;
14 int p[N];
15
16 bool cmp(it a, it b)//重载sort函数的排序
17 {
18 return a.w < b.w;
19 }
20
21 int find(int x)
22 {
23 if (p[x] != x) return p[x] = find(p[x]);
24 return p[x];
25 }
26
27 int kruskal()
28 {
29 sort(edges + 1, edges + 1 + m, cmp);
30
31 int res = 0, cnt = 0;
32 for (int i = 1; i <= m; i ++ )
33 {
34 int a = edges[i].a, b = edges[i].b, w = edges[i].w;
35
36 a = find(a), b = find(b);//查询操作
37 if (a != b)
38 {
39 p[a] = b;//合并操作
40 res += w;
41 cnt ++ ;
42 }
43 }
44 if (cnt < n - 1) return -1;
45 return res;
46 }
47
48 int main()
49 {
50 cin >> n >> m;
51
52 for (int i = 1; i <= n; i ++ ) p[i] = i;
53
54 for (int i = 1; i <= m; i ++ )
55 {
56 int a, b, w;
57 scanf("%d%d%d", &a, &b, &w);
58 edges[i] = {a, b, w};
59 }
60
61 int res = kruskal();
62
63 if (res == -1) cout << "orz" << endl;//当生成树中没有包含n - 1条边时这个生成树就是不合法的。
64 else cout << res << endl;
65 return 0;
66 }