Kruskal算法求最小生成树
c++
Kruskal算法求最小生成树
/*
* 最小生成树
* 最小生成树是图论中最为常见的问题之一,不过相比于最短路中的单源最短路和多源最短路,最小生成树算法主流仅有两种。
* 分别是 Prim 算法和 Kruskal 算法。这两个算法一个是基于点的算法,一个是基于边的算法。
* 这两者的关系有些像 最短路中 Dijkstra 和 Bellmanford。其中,Prim 算法和 Dijkstra 的流程图几乎一致。
* 此次,我们将要介绍著名的 Kruskal 算法。
*
* 算法流程:
* Step 1:
* 将所有的边按照边权大小从小到大排序。
* Step 2:
* 按照从小到大的顺序遍历所有的边,对每一个边 edge 的端点 u, v,倘若 u v 不在一个连通域内,那么就将 u v相连。
* Step 3:
* 倘若最后,所有的点都进入了同一个连通域,那么最小生成树构成。否则,最小生成树不存在。
* 算法证明:
* 对于这种迭代算法,仅需要证明其在 for 循环中,算法仍然成立即可。
* 假如说,我们边为 edge1 时候,u v 端点不在同一个连通域中,不妨考虑,有没有一种可能,不加入 edge1 使得 u v相连通了呢?
*
* 假如说,有一种解决方案,得到了最小生成树,并没有用到 edge1。但是从我们排序边这个做法可以知道,edge1 绝对是 连接 u, v代价最小的办法。
* 因此将 edge1 放到这个最小生成树中,必定可以找到环中 大于等于 edge1 权重的边,将其拿下,还是最小生成树。
* 不断迭代,可以说明,我们这样是对的。
*
* 算法复杂度:
* 算法主要复杂度是对边的排序,算法复杂度为 M log M
* 注意点:
* 这是无向图,排序和并查集
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
using namespace std;
const int N = 100010, M = 200010, INF = 0x3f3f3f3f;
class Edge {
public:
int u, v, w;
};
Edge e[M];
int n, m;
int fa[N];
bool cmp(Edge t1, Edge t2) {
return t1.w < t2.w;
}
int Find(int x) {
if (fa[x] == x) {
return x;
} else {
return fa[x] = Find(fa[x]);
}
}
void Union(int x, int y) {
x = Find(x), y = Find(y);
fa[x] = y;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1, a, b, c; i <= m; i ++ ) {
scanf("%d%d%d", &a, &b, &c);
e[i].u = a, e[i].v = b, e[i].w = c;
}
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= n; i ++ ) {
fa[i] = i;
}
int ret = 0, cnt = 0;
for (int i = 1; i <= m; i ++ ) {
if (Find(e[i].u) == Find(e[i].v)) {
continue;
} else {
Union(e[i].u, e[i].v);
ret += e[i].w;
cnt += 1;
}
}
if (cnt == n - 1) {
printf("%d\n", ret);
} else {
printf("impossible\n");
};
return 0;
}