最小生成树

定义\large\color{skyblue}\textbf{定义}

定义无向连通图的最小生成树,为边权和最小的生成树。(生成树就是连接图里的某些边,使其成为一棵包含图中所有节点的树)。

Prim\large\color{skyblue}\mathcal Prim

类似于 dij,每次加入一个距离点集 SS 最小的点。

General Idea\color{skyblue}\mathcal General \ Idea

  1. 把到所有点的距离 disiinfdis_i\gets inf
  2. 钦定一个点 uu,将 disu0dis_u\gets 0
  3. 找到最小的不属于 SSdismxdis_{mx},将 visitruevis_i\gets true
  4. 更新与 mxmx 相连的边。
  5. 构成最小生成树,退出。

朴素版本的时间复杂度为:O(n2+m)O(n^2+m)

Code\color{skyblue}\mathcal Code

#include<iostream>
#include<cstring>
using namespace std;
const int N=5e3+10,inf=0x3f3f3f3f;
int n,m;
int vis[N],f[N],dis[N],ans,a[N][N];
void prim() {
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	for(int i=1;i<=n;i++)
	{
		int mx=0;
		for(int j=1;j<=n;j++)
			if(dis[j]<dis[mx]&&!vis[j])
				mx=j;
		if(dis[mx]==inf)
		{
			ans=inf;
			break;
		}
		ans+=dis[mx];vis[mx]=1;
		for(int j=1;j<=n;j++)
			if(!vis[j]&&dis[j]>a[mx][j])
				dis[j]=a[mx][j];
	}
	if(ans!=inf)
		cout<<ans;
	else
		cout<<"impossible";
}
int main() {
	ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
	memset(a,0x3f,sizeof a);
	cin>>n>>m;
	for(int i=1,x,y,z;i<=m;i++)
		cin>>x>>y>>z,a[x][y]=a[y][x]=min(z,a[x][y]);
	prim();
	return 0;
}

在稀疏图中,nnmm 相近,使用优先队列进行优化,可以将复杂度变为 O((n+m)logn)O((n+m)\log n)。但此时需要注意:

  • 在稠密图中,请使用朴素版本。
  • 在稀疏图中,请使用堆优化版。

Code\color{skyblue}\mathcal Code

#include<iostream>
#include<cstring>
#include<queue>
#define pii pair<int,int>
using namespace std;
const int N=5e2+10,M=1e5+10;
int n,m;
int dis[N],vis[N],ans,cnt;
priority_queue<pii,vector<pii>,greater<pii>>q;
struct Node
{
	int to,next,dis;
}edge[M<<1];
int ne,head[N];
void ae(int from,int to,int dis)
{
	edge[++ne].to=to;
	edge[ne].next=head[from];
	edge[ne].dis=dis;
	head[from]=ne;
}
void prim() {
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	q.push({0,1});
	while(!q.empty()&&cnt<n)
	{
		int min_dis=q.top().first,from=q.top().second;
		q.pop();
		if(vis[from])
			continue;
		vis[from]=1;
		ans+=min_dis;
		cnt++;
		for(int i=head[from];i;i=edge[i].next)
		{
			int to=edge[i].to,tmp_dis=edge[i].dis;
			if(dis[to]>tmp_dis)
			{
				dis[to]=tmp_dis;
				q.push({dis[to],to});
			}
		}
	}
	if(cnt==n)
		cout<<ans;
	else
		cout<<"impossible";
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1,x,y,z;i<=m;i++)
	{
		cin>>x>>y>>z;
		ae(x,y,z),ae(y,x,z);
	}
	prim();
	return 0;
}

码量比较大,不是很友好。


文中模板均只输出了最小生成树的权值和,还可以实现输出等操作。

Kruskal\large\color{skyblue}\mathcal Kruskal

将边权升序排列,当加入该边无环时,加入边,直到有 n1n-1 条边。

General Idea\color{skyblue}\mathcal General \ Idea

  1. 将边按边权升序排列。
  2. 使用并查集维护所有点。遍历所有边,设当前边由 uuvv。若 find(v)=find(u)find(v)=find(u),即构成环,不加入。
  3. 加入了 n1n-1 条边,退出。

Code\color{skyblue}\mathcal Code

#include<iostream>
#include<algorithm>
using namespace std;
#define x(_) x[mp[_]]
#define y(_) y[mp[_]]
#define z(_) z[mp[_]]
const int N=1e5+10,M=2e5+10;
int n,m;
int x[M],y[M],z[M],mp[M];
int fa[N];
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
int ans,cnt;
void kruskal()
{
	for(int i=1;i<=n;i++)
		fa[i]=i;
	sort(mp+1,mp+m+1,[](int a,int b){return z[a]<z[b];});
	for(int i=1;i<=m;i++)
	{
		int fx=find(x(i)),fy=find(y(i));
		if(fx==fy) continue;
		ans+=z(i),cnt++;
		fa[fx]=fa[fy];
	}
	if(cnt<n-1)
		cout<<"orz";
	else
		cout<<ans;
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=m;mp[i]=i,i++)
		cin>>x[i]>>y[i]>>z[i];
	kruskal();
	return 0;
}

本文作者:cjrqwq

本文链接:https://www.cnblogs.com/yfzqwq/p/18492818

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   cjrqwq  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
展开
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.