题目来源:AcWing 859. Kruskal算法求最小生成树
一、题目描述
给定一个 n n n 个点 m m m 条边的无向图,图中可能存在重边和自环,边权可能为负数。
求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
给定一张边带权的无向图 G = ( V , E ) G=(V,E) G=(V,E),其中 V V V 表示图中点的集合, E E E 表示图中边的集合, n = ∣ V ∣ n=|V| n=∣V∣, m = ∣ E ∣ m=|E| m=∣E∣。
由 V V V 中的全部 n n n 个顶点和 E E E 中 n − 1 n−1 n−1 条边构成的无向连通子图被称为 G G G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G G G 的最小生成树。
输入格式
第一行包含两个整数
n
n
n 和
m
m
m。
接下来 m m m 行,每行包含三个整数 u , v , w u,v,w u,v,w,表示点 u u u 和点 v v v 之间存在一条权值为 w w w 的边。
输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible
。
数据范围
1
≤
n
≤
1
0
5
,
1≤n≤10^5,
1≤n≤105,
1
≤
m
≤
2
∗
1
0
5
,
1≤m≤2∗10^5,
1≤m≤2∗105,
图中涉及边的边权的绝对值均不超过
1000
1000
1000。
输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6
二、最小生成树 Kruskal算法
Kruskal算法非常简单:
- 将所有边按权重从小到大排序,这也是该算法时间复杂度瓶颈: O ( m l o g m ) O(mlogm) O(mlogm);
- 枚举每条边 a → b a→b a→b,权重是 c c c。如果当前 a , b a, b a,b 不在一个集合中,则将该边加入集合中(并查集)。
三、代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
int p[N];
int n, m;
struct Edge
{
int a, b, w;
bool operator < (const Edge& t) const
{
return w < t.w;
}
} edge[N];
int find(int x)
{
if (x != p[x]) p[x] = find(p[x]);
return p[x];
}
int kruskal()
{
sort(edge, edge + m);
int res = 0, cnt = 0; // cnt 存储当前已经加了多少条边
for (int i = 0; i < m; i++)
{
auto e = edge[i];
int a = e.a, b = e.b, w = e.w;
a = find(a), b = find(b);
if (a != b)
{
res += w;
cnt++;
p[a] = b;
}
}
// n 顶点相连需要 n - 1 条边
if (cnt != n - 1) return INF;
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < m; i++)
{
int a, b, w;
scanf("%d%d%d", &a, &b, &w);
edge[i] = {a, b, w};
}
// 初始化并查集
for (int i = 1; i <= n; i++) p[i] = i;
int t = kruskal();
if (t == INF) puts("impossible");
else printf("%d\n", t);
return 0;
}