最小生成树

最小生成树的介绍

生成树的定义

在一个具有$ n $个点的无向连通图中,选出$ n-1 $条边将$ n $个点连接起来,构成一棵树。

注意:只有连通图才有生成树,非连通图只有生成森林。

最小生成树的定义

我们定义无向连通图的最小生成树为边权和最小的生成树。


最小生成树算法

Kruskal算法

实现

Kruskal算法是一种常见并且好写的最小生成树算法,由Kruskal发明。该算法的基本思想是从小到大加入边,是个贪心算法。如图:

来自于OI Wiki

算法虽简单,但需要相应的数据结构来支持……具体来说,维护一个森林,查询两个结点是否在同一棵树中,连接两棵树。

抽象一点地说,维护一堆 集合,查询两个元素是否属于同一集合,合并两个集合。

其中,查询两点是否连通和连接两点可以使用并查集维护。如果使用 $ O(m \log m) 的排序算法,并且使用 O(m \log n) $ 的并查集,就可以得到时间复杂度为 $ O(m \log m) $ 的 Kruskal 算法。

证明

思路很简单,为了造出一棵最小生成树,我们从最小边权的边开始,按边权从小到大依次加入,如果某次加边产生了环,就扔掉这条边,直到加入了 $ n-1 $ 条边,即形成了一棵树。

Prim算法

实现

Prim 算法是另一种常见并且好写的最小生成树算法。该算法的基本思想是从一个结点开始,不断向已生成的树中加入离这课树最近的点(而不是 Kruskal 算法的加边),也是贪心算法。如图:

来自于OI Wiki

具体来说,每次要选择距离最小的一个结点,以及用新的边更新其他结点的距离。其实跟 Dijkstra 算法一样,每次找到距离最小的一个点,可以暴力找也可以用堆维护。暴力维护的复杂度为$ O(n^2+m) $,堆优化的复杂度为 $ O((n+m) \log n) $ 。

证明

从任意一个结点开始,将结点分成两类:已加入的,未加入的。每次从未加入的结点中,找一个与已加入的结点之间边权最小值最小的结点。然后将这个结点加入,并连上那条边权最小的边。重复 $ n-1 $ 次即可。


代码

来源:洛谷:P3366 【模板】最小生成树

Kruskal算法

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb(x) push_back(x)
#define mp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pr(x) cerr<<#x<<"="<<(x)<<endl
#define pri(x,lo) {cerr<<#x<<"={";for (int ol=0;ol<=lo;ol++)cerr<<x[ol]<<",";cerr<<"}"<<endl;}
#define inf 1000000000
#define N 1000100
int fa[N],x,y,z,nm,op,next,f1,f2,cnt,ans;
struct node
{
	int f,t,v;
}e[N];
int cmp(node a,node b)
{
	return a.v<b.v;
}
int find(int x)
{
	if(fa[x]==x)	return x;
	fa[x]=find(fa[x]);
	return fa[x];
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		cin>>x>>y>>z;
		e[++next].f=x;
		e[next].t=y;
		e[next].v=z;
	}
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=n;i++)
		fa[i]=i;
	for(int i=1;i<=m;i++)
	{
		f1=find(e[i].f);
		f2=find(e[i].t);
		if(f1!=f2)
		{
			fa[f1]=f2;
			cnt++;
			ans+=e[i].v;
			if(cnt==n-1)
				break;
		}
	}
	cout<<ans;
	return 0;
}

Prim算法

#include<iostream>
#include<cstdio>
#include<string>
#include<cmath>
#include<queue>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb(x) push_back(x)
#define mp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pr(x) cerr<<#x<<"="<<(x)<<endl
#define pri(x,lo) {cerr<<#x<<"={";for (int ol=0;ol<=lo;ol++)cerr<<x[ol]<<",";cerr<<"}"<<endl;}
#define inf 1000000000
#define N 1000100
typedef pair<int,int> pa;
priority_queue<pa,vector<pa>,greater<pa> >q;
int size,g[N],n,m,x,y,z,vis[N],len[N],sum;
struct node
{
	int next,to,v;
}e[N];
void add(int x,int y,int z)
{
	 e[++size].to=y;
	 e[size].next=g[x];
	 g[x]=size;
	 e[size].v=z;
}
void prim()
{
	for(int i=1;i<=n;i++)
		len[i]=inf;
	len[1]=0;
	q.push(pa(0,1));
	while(!q.empty())
	{
		int t=q.top().first,x=q.top().second;
		q.pop();
		if(vis[x])	continue;
		vis[x]=1;
		sum+=t;
		for(int k=g[x];k;k=e[k].next)
		{
			int y=e[k].to;
			if(e[k].v<len[y])
			{
				len[y]=e[k].v;
				q.push(pa(len[y],y));
			}
		}
	}
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>x>>y>>z;
		add(x,y,z);
		add(y,x,z); 
	}
	prim();
	cout<<sum;
	return 0;
}

—end—

文章标题:最小生成树
文章链接:https://www.laoguantx.top/%e6%9c%80%e5%b0%8f%e7%94%9f%e6%88%90%e6%a0%91.html
文章内容仅供参考,其中可能会有部分内容参考、引用其他网站,转发请务必标注作者为 “老官童鞋gogo” ,原文链接为 https://www.laoguantx.top/%e6%9c%80%e5%b0%8f%e7%94%9f%e6%88%90%e6%a0%91.html 。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇