最小生成树——kurskal

图的生成树

从连通图中删去若干边,来扣出一个树
前者是没有加边权的情况,这个时候随便删都没事,但是如果是第二张图,要求权值最小,则只能删去边权位为5的边在这里插入图片描述

经典例题

在这里插入图片描述

用以求解此类的问题的算法

kurskal算法

两点之间互有边长
先排序边长(sort就行)
过后就得到了一个边权从小到大的排序
贪心
每条边的存在使得两个点之间联通
如果加入图中不会使得这个图产生环,那么久加入
树中是不可能存在环的
想象一下你和你的爷爷是直系亲属是什么感觉
在这里插入图片描述
上图中红色的边的加入有两个不合理处
1:使得这棵树产生了环
2:2和3本来已经有公共祖先让它们进行连通了
所以说这条边是不合理的

加入这条边是否会产生环

两个本身连通的点连通后势必会存在环

算法的复杂度是O(|E|log|V|)

算法排序之后的行为就可以用并查集来维护

小小的优化?

对于一棵有n个点的树,至多有n-1条边
所以可以使用一个cnt变量来统计边数,达到了n-1立马跳出

图不是连通图

如果cnt没有达到n-1,则需要直接输出无解

kurskal算法模板

#include<bits/stdc++.h>
using namespace std;
int fa[200007],rankk[200007];
long long int n,m;
void init(int n){
	for(int i=1;i<=n;i++){
		fa[i]=i;
		rankk[i]=1;
	}
}
int find(int x){
	if(fa[x]==x)return x;
	else fa[x]=find(fa[x]);//路径压缩
}
void unite(int x,int y){
	x=find(x);
	y=find(y);
	if(x==y)return;
	if(rankk[x]<rankk[y])fa[x]=y;//高度优化
	else{
		fa[y]=x;
		if(rankk[x]==rankk[y]) rankk[x]++;
	}
}
int same(int x,int y){
	return find(x)==find(y);
}
struct edge{//边
	int u,v,cost;
}e[200007];
int cmp(edge e1,edge e2){
	return e1.cost<e2.cost;
}
long long int kruskal(){
	sort(e+1,e+1+m,cmp);
	int res=0,side=0;
	for(int i=1;i<=m;i++){
		if(same(e[i].u,e[i].v)==0){
			unite(e[i].u,e[i].v);
			res+=e[i].cost;
			++side;
		}
	}
	if(side==n-1)return res;
	else return -1;
}
int main(){
	cin>>n>>m;
	init(n);
	for(int i=1;i<=m;i++){
		cin>>e[i].u>>e[i].v>>e[i].cost;
	}
	long long int ans=kruskal();
	if(ans==-1)cout<<"orz";
	else cout<<ans;
	return 0;
}

次小生成树和最大生成树

最大生成树

只需要将排序函数cmp中的<改成>

int cmp(edge e1,edge e2){
	return e1.cost<e2.cost;
}

或者将边权输入是*-1,然后取出来的时候转回来

次小生成树

(严格)
最小生成树的边权总和为10
严格次小生成树比安全综合就必须>10
(不严格)
可以和最小生成树一样

如果不产生环就加边
求完最小生成树之后,找一个不输入最小生成树的边加入图中,势必会使得图中产生环
替换掉环中除了该边以外权值最大的一条边,至此,得到一个新的生成树

找出新的生成树中最小的就是次小生成树

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值