蒟蒻整理——最小生成树

依旧拿板子题练手:【模板】最小生成树 - 洛谷

众所周知,求最小生成树有两种方法:prim和kruscal。

Prim

使用链式前向星双向存储。

维护dis数组,存储已标记点到未标记点的最短距离(上标即为未标记点的编号)。循环过程中,考虑到重边的影响,需要进行去重操作(即只留下到达某节点所有边中的最短边),后循环查找最小边、更新答案,并利用最小边到达的节点继续去重、探索下一个最小边。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005, inf=0x7fffffff;
int n,m,x,y,z,cnt,ans,dis[5005],head[5005],vis[5005];
struct edge{
	int next,to,dis;
}e[maxn<<1];
void add(int from,int to,int dis){
	e[++cnt].next=head[from];
	e[cnt].dis=dis;
	e[cnt].to=to;
	head[from]=cnt;
}
int main(){
	cin>>n>>m;
	for(int i=0;i<m;++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); add(y,x,z);
	}
	for(int i=2;i<=n;++i) dis[i]=inf;//dis[1]为0 
	int minn, tot=0, now=1;
	vis[now]=1;
    //假设1->2,1->3
	for(int i=head[1];i;i=e[i].next)
		if(dis[e[i].to]>e[i].dis)//更新2/3的距离
			dis[e[i].to]=e[i].dis;//head的上标为节点编号 
	while(++tot<n){
		minn=inf;
		vis[now]=1;
		for(int i=1;i<=n;++i)
			if(!vis[i]&&dis[i]<minn){
				minn=dis[i];//假设最短距离为dis[2]
				now=i;
			}
		if(minn==inf){
			cout<<"orz";
			return 0;
		}
		ans+=minn;
		for(int i=head[now];i;i=e[i].next)//2->4,2->5同上操作
			if(dis[e[i].to]>e[i].dis&&!vis[e[i].to])
				dis[e[i].to]=e[i].dis;
	}
	cout<<ans;
	return 0;
} 

Kruskal

把并查集给忘了QAQ补补:

并查集分为查找和合并。

//查找就是找最早的祖先:
int find(int x){
    //while(x!=fa[x]) x=fa[x];
    //上面的式子在“辈分”较早的情况下会影响查找效率
    //推荐下面的路径压缩法,可以将链式祖先查找变为树状
    
    while(x!=fa[x]) x=fa[x]=fa[fa[x]];
    return x;
}

//合并就是让一个人的祖先认另一个人的祖先做爸爸
void merge(int x, int y){
    fa[find(x)]=find(y);
}

对于生成树,被合并的相当于进入了“大部队”——所有被合并的节点有同一个祖先。

Kruskal的思想很简单:将所有边从小到大排序,后只要不形成闭环(没进入大部队),就可以加入答案。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005, inf=0x7fffffff;
int n,m,ans,cnt,fa[5005];
struct edge{
	int from,to,dis;
}e[maxn<<1];
bool cmp(edge a,edge b){
	return a.dis<b.dis;
}
int find(int x){
	while(fa[x]!=x) x=fa[x]=fa[fa[x]];
	return x;
}
int main(){
	cin>>n>>m;
	for(int i=0;i<m;++i){
		scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].dis);
	}
	for(int i=1;i<=n;++i) fa[i]=i;
	sort(e,e+m,cmp);
	for(int i=0;i<m;++i){
		int x=find(e[i].from), y=find(e[i].to);
		if(x==y) continue;
		fa[x]=y;
		ans+=e[i].dis;
		if(++cnt==n-1){
			cout<<ans;
			return 0;
		}
	}
	cout<<"orz";
	return 0;
} 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值