最小生成树(一)

简介

在一个无向连通图中,如果存在一个连通子图包含原图中所有的节点和部分边,且这个子图不存在回路,那么我们称这个子图为原图的一棵生成树。在带权图中,所有的生成树中边权最小的一课或者几棵称为最小生成树。

Kruskal算法

算法思想

Kruskal算法比较简单, 实际上是一种贪心的方式。不断从未选的边的集合当中选择权值最小的,并且不和已选的点(初始已选的点的集合是空集)构成回路的边,将边的两个端点加入我们的已选点的集合当中,如此往复。
支撑这个算法有个小的定理,我们可以描述为:
在要求解得连通图中,任选一些点属于集合A,剩余的点属于集合B,那么我们选择一条权值最小的边,有个条件是这条边的两个顶点分属于集合A和集合B,那么这条边一定包含于这个连通图的一棵最小生成树当中。
证明可以使用替换法。

算法操作

  1. 构建点集(if necessary)。
  2. 构建边集。
  3. 按照边的权值大小进行排序。
  4. 使用并查集检查合并集合。

如何判断我们选择的边已经在已选的集合中了呢?如果我们选择的边的集合的两个端点在集合中,那么这条边一定和原来的集合构成回路,因此这条边我们是不能选择的。只需要判断roota和rootb关系即可,和之前的不矛盾。

下面两道题目练习一下,第一道是很明显的题目,练手用。
传送门:还是畅通工程

#include <cstdio>
#include <cstring>
#include <iostream>
#include <string>
#include <algorithm>
#define N 6000
using namespace std;

//最小生成树
struct Edge{
    int a,b;
    int length;
    bool operator < (const Edge &B)const{
        return length<B.length;
    }
}edge[N];

int Tree[N];  //并查集处理集合问题
int findRoot(int x){
    if(Tree[x]==-1)
        return x;
    else{
        int root=findRoot(Tree[x]);
        Tree[x]=root;
        return root;
    }
}
int main(){
    //freopen("test.txt","r",stdin);
    int n;
    while(scanf("%d",&n)!=EOF &&n!=0){
        memset(Tree,-1,sizeof(Tree));
        memset(edge,0,sizeof(edge));
        for(int i=1;i<=n*(n-1)/2;i++){
            scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].length);
        }
        sort(edge+1,edge+1+n*(n-1)/2);
        int re=0;
        for(int i=1;i<=n*(n-1)/2;i++){
            int roota=findRoot(edge[i].a);
            int rootb=findRoot(edge[i].b);
            if(roota!=rootb){
                re+=edge[i].length;
                Tree[roota]=rootb;
            }
        }
        printf("%d\n",re);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值