最小生成树之kruskal算法

这是并查集的补充应用部分。

什么是生成树&&什么是最小生成树

生成树问题是指:给你一个n个点,m条边的无向带权连通图,你需要在其中找出n-1条边组成一棵树。一个图可以有很多生成树。

最小生成树:一棵边权之和最小的生成树。最小生成树可以不唯一,但最小生成树的边权和是唯一的。

最小生成树的求法

暴力

显然,我们可以暴力枚举n-1条边,完事之后判断是否是一颗树,如果是的话再统计一下边权。时间复杂度为 Cnm ,显然过大。

其中有一个明显的剪枝,就是如果加入了当前选的这条边之后,如果已经出现了环,就退出。那现在的问题就是:如何高效地判断环呢?

一个环的前身必定是一棵树,这是显然的。一个树上的点必定在一个连通块中。那么,我们只需要看一下添加一条边时这条边的两个端点在不在同一个连通块中即可。对这个操作,你当然可以用LCT维护动态并查集,或者可持久化一下,我们只能用DFS。

贪心

经过前人伟大的YY,我们得出了一个结论:
如果我们按从小到大的顺序枚举边,则得到的就是最小生成树。算法流程如下:

  1. 把所有的边权排序
  2. 重复以下操作,直到已经加入n-1条边

    1. 选取一条权值最小的未判断过的边。
    2. 如果这条边的两个端点不在同一个连通块中,则加入这条边,并用并查集将两个端点合并。否则不做任何事。

代码实现(伪代码)如下:

const int maxn=...;
const int maxm=...;
struct edge{
    int s,t,len;
};
edge E[maxm];
int fa[maxn];
inline int ask(int x){
    return fa[x]==x?x:fa[x]=ask(fa[x]);
}//并查集实现
bool cmp(edge a,edge b){
    return a.len<b.len;
}
int main(){
    //n是点数,m是边数
    init();//读入
    sort(E+1,E+m+1,cmp);
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1;i<=m;i++){//可以直接从1-m循环
        int x=ask(E[i].s),y=ask(E[i].t);
        if (x!=y){
            fa[x]=y;
            ans+=E[i].len;
        }
    }
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值