# Road Map
# 最小生成树
边权和最小的生成树
Minimum Spanning Tree, MST
# Kruskal 算法
Kruskal 算法是一种常见并且好写的最小生成树算法,由 Kruskal 发明。该算法的基本思想是从小到大加入边,是个贪心算法。
算法思路:
- 将所有边按权重从小到大排序 O(mlogm)
- 枚举每条边
u <--> v
,权重 w- if a,b 不连通 - 将这条边加入集合中 前置知识:贪心、并查集
# 应用场景
稀疏图的 最小生成树
# 代码实现
- AcWing 859. Kruskal 算法求最小生成树
/*
res 最小生成树中的权重之和
cnt 当前加了多少条边
1.将所有边按权重排序O(mlogm)
2.枚举每条边(并查集应用)
if a,b 不连通
加入集合
3.需重载<
bool operator < (const Edge &C) const {
return w < C.w;
}
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10, M = 2e5+10, INF = 0x3f3f3f3f;
int n, m; // n个点,m条边
int f[N]; // 并查集
struct Edge{
int u, v, w;
bool operator < (const Edge&e) const{
return w < e.w; // 按权重从小到大排序
}
}edges[M];
int find(int x){
if (x == f[x]) return x;
return f[x] = find(f[x]);
}
int kruskal(){
sort(edges, edges+m);
for (int i =0; i<= n; i++)f[i] = i; // 初始化并查集
int res = 0, cnt = 0; // res:最小生成树权重之和;cnt:增加了多少条边
for (int i = 0; i <m; i++){
auto e = edges[i];
int p = find(e.u), q = find(e.v);
if (p != q){
f[q] = p;
res += e.w;
cnt++;
}
}
if (cnt < n-1) return INF;
return res;
}
int main(){
cin >> n >> m;
int u, v, w;
for(int i = 0; i<m; i++){
cin >> u >> v >> w;
edges[i] = {u, v, w};
}
int ans = kruskal();
if (ans > INF/2) puts("impossible");
else cout << ans << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# Prim 算法
# 朴素版 Prim 算法
# 代码实现
- Acwing 858. Prim 算法求最小生成树
/*
S:当前已经在联通块中的所有点的集合
1. dist[i] = inf
2. for n 次
t<-S外离S最近的点
利用t更新S外点到S的距离
st[t] = true
n次迭代之后所有点都已加入到S中
联系:Dijkstra算法是更新到起始点的距离,Prim是更新到集合S的距离
*/
#include<bits/stdc++.h>
using namespace std;
const int N = 510, INF = 0x3f3f3f3f;
int n, m; // n个点,m条边
int g[N][N];// 邻接矩阵
int dist[N]; // 存储其他点到S的距离
bool st[N]; // 是否已得到最短距离
int prim(){
memset(dist, INF, sizeof dist);
int res = 0;// 如果图不连通,返回INF,否则返回res
for (int i = 0; i<n; i++){ // n次迭代,将n个点加入集合
int t = -1; // 找到距离集合最近的点
for (int j =1; j <= n; j++){
if (!st[j] && (t == -1 || dist[t] > dist[j])){
t = j;
}
}
// 找到了距离集合S 最近的点t
if (i && dist[t] == INF) return INF; // 不连通
if (i) res += dist[t];
st[t] = true;
// 更新到集合S的最短距离
for (int j = 1; j <=n; j++) dist[j] = min(dist[j], g[t][j]);
}
return res;
}
int main(){
cin >> n >> m;
for (int i = 1; i <=n ;i++){
for (int j = 1; j <=n; j++){
if (i == j) g[i][j] = 0;
else g[i][j] = INF;
}
}
int u, v, w;
while (m--){
cin >> u >> v>> w;
g[u][v] = g[v][u] = min(g[u][v], w);
}
int t = prim();
if (t == INF) puts("impossible");
else cout << t << endl;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58