kruskal算法例题

点击查看代码
/*
输入样例:
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;
}
posted @ 2022-09-30 22:52  zhaoo_o  阅读(49)  评论(0编辑  收藏  举报