2025-02-06最小生成树的典型应用
最小生成树的典型应用
主要内容
1. 最小生成树的基本概念
- 定义:在一个加权连通图中,最小生成树是一个包含图中所有顶点的子图,且边的总权重最小。
- 应用场景:网络设计、城市规划、通信线路铺设等,目标是连接所有节点,同时最小化总成本。
- 一般思路:
2. 最小生成树的两种主要算法
Prim算法
适用场景:适用于稠密图(边数较多的图)。
时间复杂度:O(n2),可以通过堆优化降低到 O(mlogn)。
核心思想:从一个起点开始,逐步扩展最小生成树,每次选择与当前生成树相连的最小权重边。
代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19int prim() {
int res = 0;
memset(dist, 0x3f, sizeof dist);
dist[1] = 0;
for (int i = 0; i < n; i++) {
int t = -1;
for (int j = 0; j < n; j++) {
if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
}
res += dist[t];
st[t] = true;
for (int j = 0; j < n; j++) dist[j] = min(dist[j], w[t][j]);
}
return res;
}
Kruskal算法
适用场景:适用于稀疏图(边数较少的图)。
时间复杂度:O(mlogm),其中 m 是边的数量。
核心思想:将所有边按权重从小到大排序,依次选择不形成环的边加入最小生成树。
代码实现:
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
29int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void work() {
cin >> n >> m;
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 0; i < m; i++) {
int a, b, w;
cin >> a >> b >> w;
e[i] = {a, b, w};
}
sort(e, e + m);
int res = 0;
for (int i = 0; i < m; i++) {
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if (a != b) {
p[a] = b;
res += w;
}
}
cout << res << endl;
}
3. 并查集的使用
功能:用于管理不相交集合,支持快速合并和查找操作。
核心操作:
- 查找操作(Find):通过路径压缩优化查找效率。
- 合并操作(Union):通过按秩合并优化合并效率。
代码实现:
1
2
3
4
5
6
7
8
9
10
11int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
void union_sets(int a, int b) {
int rootA = find(a), rootB = find(b);
if (rootA != rootB) {
p[rootA] = rootB;
}
}
4. 最小生成树的变种问题
最大边权最小化:
问题描述:在维持连通性的情况下,使最大边权最小。
解决方案:使用Kruskal算法,记录最后加入的边的权重。
代码实现:
1
2
3
4
5
6
7
8
9
10
11int max = -1, s = 0;
for (int i = 0; i < m; i++) {
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if (a != b) {
p[a] = b;
s++;
max = w;
}
}
cout << s << " " << max << endl;
必选边和可选边:
问题描述:某些边必须选择,某些边可以选择。
解决方案:先处理必选边,再处理可选边。
代码实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24int res = 0;
for (int i = 0; i < m; i++) {
int f, a, b, w;
cin >> f >> a >> b >> w;
if (f == 1) {
int aa = find(a), bb = find(b);
if (aa != bb) p[aa] = bb;
res += w;
} else {
e[cnt++] = {a, b, w};
}
}
sort(e, e + cnt);
for (int i = 0; i < cnt; i++) {
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if (a != b) {
res += w;
p[a] = b;
}
}
cout << res << endl;
5. 二维坐标映射为一维数组
问题描述:在某些问题中,需要将二维坐标映射为一维数组的下标。
解决方案
1
2
3
4int ids[K][K];
for (int i = 1, t = 1; i <= n; i++)
for (int j = 1; j <= m; j++, t++)
ids[i][j] = t;
6. 总结
- 最小生成树是图论中的一个重要概念,广泛应用于网络设计和资源优化。
- Prim算法和Kruskal算法是两种常用的最小生成树算法,各有优劣,适用于不同类型的图。
- 并查集是处理最小生成树问题时常用的辅助数据结构,支持高效的合并和查找操作。
- 变种问题(如最大边权最小化、必选边和可选边)可以通过对标准算法的扩展来解决。
- 二维坐标映射是处理网格问题时的常用技巧,可以简化问题的复杂度。
例题
最短网络
1 |
|
局域网
1 |
|
繁忙的都市
1 |
|
联络员
1 |
|
连接格点
1 |
|
2025-02-06最小生成树的典型应用
http://666xz666.github.io/2025/02/06/最小生成树的典型应用/