@toc
定义
给定一个带权图,满足以下条件:
- 1.保证图中所有的点都联通
- 2.在满足条件1的情况下尽可能去掉多的边,使得所有的边权之和最小,即$\Sigma_{i=1}^{i<=m}w_i$最小。
Kruskal算法
Kruskal是基于贪心的思想,根据以上的定义描述依次枚举$1-m$条边,如果两个点没有存在于一个连通分量中,那么就连上这一条边。
此算法的难点在于查询两个点是否在一个连通分量中,朴素思想可以使用$DFS$/$BFS$进行遍历,但是会使得时间复杂度非常高,此时可以使用并查集进行维护,优化时间。
算法流程
1.建立并查集,初始化$fa[i] = i$.
2.按边权从小到大枚举。
3.如果$(u, v, w)$中$u$和$v$不连通,就合并$u$,$v$所在的集合,$ans$累加上$w$.
4.否则直接跳过
5.如果已经加上了$n - 1$条边,那么就退出,得到答案。
时间复杂度为 $O(m log m)$.
具体实现
建立结构体存边
1 2 3 4 5 6
| struct node { int u, v, w; } dis[MAXM]; bool cmp(node x, node y) { return x.w < y.w; }
|
并查集维护
1 2 3 4 5 6 7 8 9 10 11 12 13
| int FindSet(int v) { if(fa[v] == v) return v; else { return fa[v] = FindSet(fa[v]); } } bool UnionSet(int v, int u) { int x = FindSet(v); int y = FindSet(u); if(x == y) return 0; fa[x] = fa[y]; return 1; }
|
完整代码
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
| #include <cstdio> #include <iostream> #include <algorithm> using namespace std; const int MAXN = 105; const int MANM = MAXN * MAXN;
int n, m, tot, ans; int fa[MAXN];
struct node { int u, v, w; } dis[MAXM]; bool cmp(node x, node y) { return x.w < y.w; }
int FindSet(int v) { if(fa[v] == v) return v; else { return fa[v] = FindSet(fa[v]); } } bool UnionSet(int v, int u) { int x = FindSet(v); int y = FindSet(u); if(x == y) return 0; fa[y] = fa[x]; return 1; }
int main() { scanf("%d %d", &n, &m); for(int i = 1; i <= m; i++) { scanf("%d %d %d", &dis[i].u, &dis[i].v, &dis[i].w); } sort(dis + 1, dis + 1 + m, cmp) ; for(int i = 1; i <= n; i++) fa[i] = i; for(int i = 1; i <= m; i++) { if(UnionSet(dis[i].u, dis[i].v)) { ans += dis[i].w; tot ++; } if(tot == n - 1) break; } printf("%d\n", ans); return 0; }
|