图论·prim+Kruskal

prim算法

适用:最小生成树问题

  • 树:不是连通图,说明要用尽可能少的边,但至少需要v-1条边
  • 最小:指边的权值之和最小

核心思想:贪心

  • 树的问题:就建立v-1条边
  • 最小的问题:每次只选择距离生成树最近的一条边,达到局部最优,最终贪心出来全局最优

核心操作

  • 选点:距离生成树最近的结点
  • 加入集合:将结点加入生成树的集合中,并且更新isinTree数组,避免重复加入
  • 更新minDist数组:更新minDist(各个结点1~v距离生成树的最小距离),只更新未加入结点的距离

个人代码

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int v, e, a, b, w,ans=0;
void solve() {
	cin >> v >> e;
	vector<vector<int>>grid(v + 1, vector<int>(v + 1, 10009));//邻接矩阵
	vector<int>minDist(v + 1, 10009);
	vector<bool>isinTree(v + 1, false);
	while (e--) {
		cin >> a >> b >> w;
		grid[a][b] = w; grid[b][a] = w;
	}
	for (int i = 1; i < v; i++) {//至少v-1次连通,不要建多了路
		int minValue = INT_MAX;
		int cur = -1;
		for (int j = 1; j <= v; j++) {//选距离生成树最小的结点,这种写法是下标靠后的结点
			if (!isinTree[j] && minDist[j] < minValue) {
				minValue = minDist[j];
				cur = j;
			}
		}
		isinTree[cur] = true;//cur是选择的结点
		for (int j = 1; j <= v; j++) {//更新距离
			if (!isinTree[j] && grid[cur][j] < minDist[j]) {
				minDist[j] = grid[cur][j];
			}
		}
	}
	for (int i = 2; i <= v; i++) {
		ans += minDist[i];//最小生成树权值
	}
	cout << ans;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);
	solve();
	return 0;
}

输出连接的边

  • 定义一个pre数组,表示结点的前驱(就像并查集一样)
  • 更新边时,也把前驱也更新了
for (int j = 1; j <= v; j++) {//更新距离
	if (!isinTree[j] && grid[cur][j] < minDist[j]) {
		minDist[j] = grid[cur][j];
		pre[j] = cur;
	}
}

//----------输出代码如下------------
for (int i = 2; i <= v; i++) {
	cout << pre[i]<<"->"<<i << endl;
	ans += minDist[i];
}
cout << ans;

本文参考自代码随想录

Kruskal算法

适用:最小生成树问题

核心思想:贪心

  • 直接了当的贪心:直接加入最小权值的边
  • 使用并查集防止生成树成环

核心操作

  • 并查集的定义:init,find,merge三大基本操作
  • 对边权值进行排序:定义一个结构体,使用C++的sort函数进行自定义排序

个人代码

using namespace std;
using ll = long long;
int v, e, a, b, w,ans=0;
int pre[10009];
struct Edge {
	int v1, v2, val;
};
vector<Edge>edges;
void init() {
	for (int i = 1; i <= v; i++) {
		pre[i] = i;
	}
}
int find(int x) {
	return pre[x] = pre[x] == x ? x : find(pre[x]);
}
void merge(int x,int y) {
	x = find(x);
	y = find(y);
	if (x == y) return;
	pre[x] = y;
}

void solve() {
	cin >> v >> e;
	init();//别忘记初始化pre数组!
	while (e--) {
		cin >> a >> b >> w;
		edges.push_back({ a,b,w });
	}
	sort(edges.begin(), edges.end(), [&](const Edge &a, const Edge&b) {//lambda表达式
		return a.val < b.val;
		});
	for (int i = 0; i < edges.size(); i++) {
		int x = find(edges[i].v1);
		int y = find(edges[i].v2);
		if (x != y) {
			merge(x, y);
			ans += edges[i].val;
		}
	}
	cout << ans;
}
int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(0); std::cout.tie(0);
	solve();
	return 0;
}

输出连接的边

	for (int i = 0; i < edges.size(); i++) {
		int x = find(edges[i].v1);
		int y = find(edges[i].v2);
		if (x != y) {
			merge(x, y);
			ans += edges[i].val;
			cout<<edges[i].v1<<"->"<<edges[i].v2<<endl;//本来就是对边操作,直接输出就好了
		}
	}

参考于代码随想录

  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值