点击查看代码
/*
输入样例:
6 10
0 1 4
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3
输出样例:
11
*/
#include<cstdio>
#include<algorithm>
#pragma warning(disable:4996)
using namespace std;
const int maxn = 110; //最多不超过100条边
const int INF = 0x3fffffff; //表示无穷大
struct edge { //边的定义
int u, v; //边的两个端点编号
int cost; //边权
}E[maxn]; //存储图中所有边的信息,最多有maxn条边
bool cmp(edge a, edge b) { //对图中的边按边权递增排序
return a.cost < b.cost;
}
//并查集,检查测试边的两个端点是否在同一个连通块(集合)中,
//如果不是,则测试边符合要求,将两个端点合并为一个集合,等价于将测试边加进最小生成树中
int father[maxn]; //并查集数组,存储每个顶点的父顶点编号
int findFather(int x) { //并查集查询函数
int a = x;
while (x != father[x]) {
x = father[x];
}
//路径压缩
while (a != father[a]) {
int z = a;
a = father[a];
father[z] = x;
}
return x;
}
//kruskal算法,函数返回最小生成树的边权之和。图G的顶点数为n,边数为m
int kruskal(int n, int m) {
int ans = 0, Num_Edge = 0; //边权之和,最小生成树当前的边数
for (int i = 0; i < n; i++) { //顶点编号从0~n-1
father[i] = i;
}
sort(E, E + m, cmp); //对E[]的边按边权从小到大排序
for (int i = 0; i < m; i++) { //枚举所有边,因为已经对边权按递增排序,所以按边权从小到大选择测试边
int faU = findFather(E[i].u); //测试边i的端点u
int faV = findFather(E[i].v); //测试边i的端点v
if (faU != faV) { //如果u和v不在同一个连通块内,则测试边符合要求
father[faU] = faV; //将u和v合并为一个集合(等价于将测试边加进最小生成树中)
ans += E[i].cost; //累加边权之和
Num_Edge++; //当前生成树的边数加一
if (Num_Edge == n - 1) break; //当边数等于顶点数减一时,可以结束算法,最小生成树查找完成
}
}
if (Num_Edge != n - 1) return -1; //如果测试边全部查找完,生成树的边数不等于顶点数减一,则最小生成树无解,返回-1
else return ans; //否则返回最小生成树的边权之和
}
int main() {
int n, m;
scanf("%d%d", &n, &m); //顶点数和边数
for (int i = 0; i < m; i++) {
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].cost); //读取每条边的两个端点和边权
}
int ans = kruskal(n, m); //使用kruskal()算法求解最小生成树的边权之和
printf("%d\n", ans);
return 0;
}