C++ Kruskal算法求最小生成树

听了雨巨的课,来做个笔记。

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有
保持图连通的最少的边。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
                                                                     ————百度百科
Kruskal算法简述
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:
先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则
它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶
点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若
该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直
至森林中只有一棵树,也即子图中含有 n-1条边为止。
                                                                    ————百度百科

简单来说将连通图的每一条边按边权的大小,从小到大排列,每次选取还未选取的边中边权最小的边,判断一下,如果选这条边会和已选的边形成环,(或者说所选的这条边连接的两个点已经在一个集合里了),则不选,pass,否则选取。
例如:
首先我们考虑边权为 1 连接 1- 3 的边,加入
然后考虑边权为 2 连接 4-6 的边,符合要求,加入
然后考虑边权为 3 连接 2-5 的边,符合要求,加入
然后考虑边权为 4 连接 3-6 的边,符合要求,加入
然后考虑边权为 5 连接 3-4 的边,显然 3 和 4 在此之前就在一个集合里了,pass
然后考虑边权为 5 连接 1-4 的边,显然 1 和 4 在此之前就在一个集合里了,pass
然后考虑边权为 5 连接 2-3 的边,符合要求,加入
那么这就已经得到了一棵最小生成树
在这里插入图片描述
用结构体,存下每一条边,用并查集判断两个结点是否在一个集合中

来个例题:https://ac.nowcoder.com/acm/problem/15108

Description

随着如今社会的不断变化,交通问题也变得越来越重要,所以市长决定建设一些公路来方便各个城市之间的贸易和交易。虽然市长的想法很好,但是他也遇到了一般人也经常头疼的问题,那就是手头的经费有限……在规划过程中,设计师们已经预算出部分城市之间建设公路的经费需求。现在市长想知道,它能不能将他的m个城市在有限的经费内实现公路交通。如果可以的话,输出Yes,否则输出No(两个城市不一定要直接的公路相连,间接公路到达也可以。)

Input

测试输入包含多条测试数据
每个测试数据的第1行分别给出可用的经费c(<1000000),道路数目n(n<10000),以及城市数目m(<100)。
接下来的n行给出建立公路的成本信息,每行给出三个整数,分别是相连的两个城市v1、v2(0<v1,v2<=m)以及建设公路所需的成本h(h<100)。

Output

对每个测试用例,输出Yes或No。

Sample Input
20 10 5
1 2 6
1 3 3
1 4 4
1 5 5
2 3 7
2 4 7
2 5 8
3 4 6
3 5 9
4 5 2
Sanple Output
Yes
Solution

直接上Kruskal算法模板

Code
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
int c, n, m;
struct node
{
	int s, e, w;
	bool operator<(const node a)const {
		return w < a.w;
	}
}edge[10010];
int fa[110];
int find(int x) {
	return fa[x] == x ? x : find(fa[x]);
}
void merge(int x, int y) {
	fa[find(x)] = find(y);
}
int Kruskal() {
	int cnt = 0;//边数
	int sum = 0;//总价值
	for (int i = 0; i < m; i++) fa[i] = i;
	sort(edge + 1, edge + n + 1);
	for (int i = 1; i <= n; i++) {
		int s = edge[i].s, e = edge[i].e;
		if (find(s) != find(e)) {//判断一下
			merge(s, e);
			++cnt;
			sum += edge[i].w;
			if (cnt >= n - 1) break;
		}
	}
	return sum;
}
int main() {
	while (cin >> c >> n >> m) {
		for (int i = 1; i <= n; i++) {
			cin >> edge[i].s >> edge[i].e >> edge[i].w;
		}
		if (Kruskal() > c) cout << "No" << endl;
		else cout << "Yes" << endl;
	}
	return 0;
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kruskal算法是一种基于贪心思想的最小生成树算法,其基本思路是将所有边按照权值从小到大排序,然后依次加入到最小生成树中,如果加入后形成了环,则不加入该边。 具体实现步骤如下: 1. 将所有边按照权值从小到大排序。 2. 初始化一个并查集,每个节点都是一个单独的集合。 3. 依次取出排序后的边,如果该边的两个端点不在同一个集合中,则将其加入最小生成树中,并将这两个端点所在的集合合并。 4. 最后得到的最小生成树就是答案。 下面是C++代码实现: ```c++ #include <iostream> #include <algorithm> #include <vector> using namespace std; const int N = 100010; int n, m; int p[N]; struct Edge { int a, b, w; bool operator< (const Edge& e) const { return w < e.w; } }; vector<Edge> edges; int find(int x) { if (p[x] != x) p[x] = find(p[x]); return p[x]; } int kruskal() { sort(edges.begin(), edges.end()); for (int i = 1; i <= n; i++) p[i] = i; int res = 0, cnt = 0; for (auto& e : edges) { int fa = find(e.a), fb = find(e.b); if (fa != fb) { p[fa] = fb; res += e.w; cnt++; if (cnt == n - 1) break; } } if (cnt < n - 1) return -1; // 无法构成生成树 return res; } int main() { cin >> n >> m; while (m--) { int a, b, w; cin >> a >> b >> w; edges.push_back({a, b, w}); } int res = kruskal(); if (res == -1) cout << "impossible" << endl; else cout << res << endl; return 0; } ``` 时间复杂度为O(mlogm),其中m为边数,因为需要对所有边进行排序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值