最小生成树

定义

\quad 在无向图中,连通且不含圈的图称为树(Tree)。给定无向图G=(V,E),连接G中所有点,且边集是E的子集的树称为G的生成树(Spanning Tree),且权值最小的生成树称为最小生成树(Minimal Spanning Tree,MST)。构造MST的算法有很多。以Kruskal为例进行介绍。

求解

\quad Kruskal的第一步是给所有的边按照权值大小的顺序排序。接下来从小到大考虑每条边(u,v)。

  • 情况1: u和v在同一个连通分量中,那么加入(u,v)之后会形成环,因此不能选择。
  • 情况2: 如果u和v不在同一个连通分量中,那么加入(u,v)一定是最优的。

伪代码描述如下:

将所有的边排序,记第i小的边为e[i](1<i<m)
初始化MST为空
初始化连通分量,让每个点自成 一个独立的连通分量
for(int i=0;i<m;i++){
    if(e[i].u和e[i].v不在同一个连通分量){
		把边e[i]加入到MST中
		合并e[i]和e[u]所在的连通分量
    }
}

在上面的伪代码中,最关键的地方在于“连通分量的查询与合并”:需要知道任意两个点是否在同一个连通分量中,还需要合并两个连通分量。有一种很简洁的数据结构可以解决这个问题 使用并查集 。把每一个连通分量看做一个集合,该集合包含了连通分量中所有的点。这些点两两相通,而具体的连接方式无关紧要,就好比集合中的元素没有先后顺序之分,只有“属于”和“不属于”的区别。

具体实现:
边排序之后,第i小的边的序号保存在r[i]中(间接排序,排序的关键字是对象的“代号”,而不是对象本身)。
在这里插入图片描述
r[]初始化为{1,2,3,4,5,6},经过排序后变成{2,4,5,3,1,6}。r[0]=2,代表最小的边为原始数据的第二条边。

代码

#include <iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn1 105
#define maxn2 105
struct node{
    int u,v,w;
}G[maxn1];
int n,m,r[maxn2],p[maxn1];
int find_(int x){
    return p[x]==x?x:p[x]=find_(p[x]);
}
int cmp(const int i,const int j){
    return G[i].w<G[j].w;
}
int main()
{
    while(scanf("%d%d",&n,&m)==1){
        int ans=0;
        for(int i=0;i<m;i++){
            scanf("%d%d%d",&G[i].u,&G[i].v,&G[i].w);
            r[i]=i;
        }
        for(int i=0;i<n;i++){
            p[i]=i;
        }
        sort(r,r+m,cmp);
        for(int i=0;i<m;i++){
            int e=r[i];
            int x=find_(G[e].u);
            int y=find_(G[e].v);
            if(x!=y){
                ans+=G[e].w;
                y=p[x];
            }
        }
        printf("%d",ans);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值