最小生成树算法

0|1今天复习了最小生成树算法,最小生成树算法分为kruskal和prim两种。由于kruskal需要并查集实现。先给出几个并查集的基本操作:

1|01.并查集的初始化

for(int i=1;i<=n;i++) father[i]=i;//先让每一个元素自己为一个集合

2|02.查找x所属集合

int findfather(int x) { if(father[x]!=x) father[x]=findfather(father[x]); return father[x]; }

3|03.将y合并到x的集合

void join(int x,int y) { int fx=findfather(x); int fy=findfather(y); if(fx!=fy) father[fx]=fy; }

3|1步入正题

4|0prim

4|1个人觉得Prim和最短路中的dijkstra很像,由于速度问题,所以这里我用边表存图。Prim的思想是将任意节点作为根,再找出与之相邻的所有边(用一遍循环即可),再将新节点更新并以此节点作为根继续搜,维护一个数组:dis,作用为已用点到未用点的最短距离。

4|2证明:Prim算法之所以是正确的,主要基于一个判断:对于任意一个顶点v,连接到该顶点的所有边中的一条最短边(v, vj)必然属于最小生成树(即任意一个属于最小生成树的连通子图,从外部连接到该连通子图的所有边中的一条最短边必然属于最小生成树)

4|3代码实现

#include<bits/stdc++.h> #define inf 0x7fffffff int nxt[500010]; int head[500010]; int to[500010]; int edge[500010]; int vis[500010]; int t=1; int cnt,n,m; int dis[500010]; int minn; int ans; int now; using namespace std; inline void add(int x,int y,int u) { cnt++; nxt[cnt]=head[x]; head[x]=cnt; to[cnt]=y; edge[cnt]=u; } inline void prim() { for(int i=2;i<=n;i++) { dis[i]=inf; } vis[1]=1; for(int i=head[1];i;i=nxt[i]) { dis[to[i]]=min(dis[to[i]],edge[i]); } now=1; while(t<n) { t++; minn=inf; vis[now]=1; for(int i=1;i<=n;i++) { if(dis[i]<minn&&!vis[i]) { now=i; minn=dis[i]; } } vis[now]=1; ans+=minn; for(int i=head[now];i;i=nxt[i]) { if(!vis[to[i]]&&dis[to[i]]>edge[i]) { dis[to[i]]=edge[i]; } } } } int main() { memset(edge,0,sizeof edge); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } prim(); printf("%d",ans); return 0; }

4|4Kruskal

4|5Kruskal算法的思想比Prin好理解一些。先把边按照权值进行排序,用贪心的思想优先选取权值较小的边,并依次连接,若出现环则跳过此边(用并查集来判断是否存在环)继续搜,直到已经使用的边的数量比总点数少一即可。

4|6证明:刚刚有提到:如果某个连通图属于最小生成树,那么所有从外部连接到该连通图的边中的一条最短的边必然属于最小生成树。所以不难发现,当最小生成树被拆分成彼此独立的若干个连通分量的时候,所有能够连接任意两个连通分量的边中的一条最短边必然属于最小生成树

4|7代码实现

#include<bits/stdc++.h> using namespace std; #define re register #define il inline il int read() { re int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x*f; } struct Edge { int u,v,w; }edge[200005]; int fa[5005],n,m,ans,eu,ev,cnt; il bool cmp(Edge a,Edge b) { return a.w<b.w; } il int find(int x) { while(x!=fa[x]) x=fa[x]=fa[fa[x]]; return x; } il void kruskal() { sort(edge,edge+m,cmp); //将边的权值排序 for(re int i=0;i<m;i++) { eu=find(edge[i].u), ev=find(edge[i].v); if(eu==ev) { continue; } //若出现两个点已经联通了,则说明这一条边不需要了 ans+=edge[i].w; //将此边权计入答案 fa[ev]=eu; //将eu、ev合并 if(++cnt==n-1) { break; } //循环结束条件,及边数为点数减一时 } } int main() { n=read(),m=read(); for(re int i=1;i<=n;i++) { fa[i]=i; } for(re int i=0;i<m;i++) { edge[i].u=read(),edge[i].v=read(),edge[i].w=read(); } kruskal(); printf("%d",ans); return 0; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17777012.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示