寒假算法题练习5

最小生成树及模板题

一、最小生成树简介

一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。 最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。

通俗的说就是有n个结点的带权无向图,用n-1条边连通,即为生成树,其中权值最小的生成树就是最小生成树。这篇文章介绍的是kruskal算法。

二、模板题目

题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz。
输入格式
第一行包含两个整数 N,MN,MN,M,表示该图共有 NNN 个结点和 MMM 条无向边。
接下来 MMM 行每行包含三个整数 Xi,Yi,ZiX_i,Y_i,Z_iXi​,Yi​,Zi​,表示有一条长度为 ZiZ_iZi​ 的无向边连接结点 Xi,YiX_i,Y_iXi​,Yi​。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz。
输入输出样例
输入
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
输出
7
样例解释:
在这里插入图片描述

所以最小生成树的总边权为 2+2+3=7。

解题过程:
kruskal算法是先以边的权值对边进行排序,然后先选取权值较小的边,依次连接,并利用并查集来判断是否为环,若形成了环则跳过此边,直到已经使用的边的数量为总结点数减一。
先用结构体存边,存点:

struct Node {
	 int rnk, pre;
	 void init(int x) {
		 rnk = 0, pre = x;
	 }
 } T[MAX_N];

struct edge {
	 int head, tail, value;
} E[MAX_M];

用并查集判断是否为环(Same):

 int Root(int x) {
 	if (T[x].pre != x) T[x].pre = Root(T[x].pre);
 	return T[x].pre;
 }
 
 bool Same(int x, int y) {
	 return Root(x) == Root(y);
 }

全部代码:

 #include<bits/stdc++.h>
 #define MAX_N 5005
 #define MAX_M 200005
 using namespace std;
 struct DSU {
	 struct Node {
	 int rnk, pre;
	 void init(int x) {
		 rnk = 0, pre = x;
	 }
 } T[MAX_N];
 void Build(int n) {
	 for (int i = 1; i <= n; i++)
	 	 T[i].init(i);
 }
 int Root(int x) {
	 if (T[x].pre != x) 
	 	T[x].pre = Root(T[x].pre);
	 return T[x].pre;
 }
 void Unite(int x, int y) {
	 x = Root(x), y = Root(y);
	 if (x == y) return;
	 if (T[x].rnk > T[y].rnk) T[y].pre = x;
	 else {
		 T[x].pre = y;
 		 if (T[x].rnk == T[y].rnk)
 		 	T[y].rnk++;
	 }
 }
 bool Same(int x, int y) {
 	return Root(x) == Root(y);
 }
 } dsu;

 struct edge {
	 int head, tail, value;
 } E[MAX_M];
 bool cmp(edge x, edge y) {
	 return x.value < y.value;
 }
 int kruskal(int n, int m) {
	 dsu.Build(n);
	 sort(E, E + m, cmp);
	 int ans = 0, js = 1;
	 for (int i = 0; i < m && js < n; i++) {
		 if (!dsu.Same(E[i].head, E[i].tail)) {
		 ans += E[i].value, js++;
		 dsu.Unite(E[i].head, E[i].tail);
		 }
	 }
	 if (js < n) return -1;
	 	else return ans;
 }
 int main(){
	int n,m,xi,yi,zi;
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		cin >> E[i].head >> E[i].tail >> E[i].value;
	}
	cout << kruskal(n,m);
 }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

asdfghtyjukilo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值