搜索与图论 Kruskal算法求最小生成树

Kruskal算法求最小生成树

给定一个 n 个点 m 条边的无向图,图中可能存在重边和自环,边权可能为负数。

求最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

给定一张边带权的无向图 G=(V,E),其中 V 表示图中点的集合,E 表示图中边的集合,n=|V|m=|E|

V 中的全部 b 个顶点和 En−1 条边构成的无向连通子图被称为 G 的一棵生成树,其中边的权值之和最小的生成树被称为无向图 G 的最小生成树。

输入格式
第一行包含两个整数 nm

接下来 m 行,每行包含三个整数 u,v,w,表示点 u 和点 v 之间存在一条权值为 w 的边。

输出格式
共一行,若存在最小生成树,则输出一个整数,表示最小生成树的树边权重之和,如果最小生成树不存在则输出 impossible

数据范围
1≤n≤105,
1≤m≤2∗105,
图中涉及边的边权的绝对值均不超过 1000。

输入样例:

4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4

输出样例:

6

Kruskal算法求最小生成树:

  1. 维护一个连通块儿集合,代表最小生成树,初始为空。
  2. 将所有的边按权重从小到大排序。
  3. 遍历每条边 (a,b,w: a,b两点之间的边,权重为w ),如果a和b不在连通块儿中(a,b不连通),就将a,b边加入连通块中。
  4. 直到最后连通块儿中的边数为 n-1,则该图有最小生成树,反之则没有。

代码:C++:
使用最小堆来存储边。

#include<bits/stdc++.h>
using namespace std;
const int N = 100010,INF=0x3f3f3f3f;
typedef pair<int,pair<int,int>> PII;
int n,m,res; //res记录最小生成树的边长和。
priority_queue<PII,vector<PII>,greater<PII> > heap;
//堆中存储着 {权值,{两个点}} {w,{a,b}}
int fa[N],cnt[N];
//fa[]:并查集,记录x的节点的父节点
//cnt[]:记录根节点所在连通块中的点数
int get(int x) //找到x的根节点
{
    if(x==fa[x]) return x;
    return fa[x]=get(fa[x]);
}
void merge(int x,int y) //连通x,y两点,并更新连通块儿中点的数量。
{
    int a=get(x),b=get(y);
    fa[b]=a;
    cnt[a]+=cnt[b];
}
int kruskal()
{
    while(heap.size())
    {
        auto t=heap.top();
        heap.pop();
        
        int c=t.first,a=t.second.first,b=t.second.second;
        
        if(get(a)==get(b)) continue;
        res+=c;
        merge(a,b);
    }
    if(cnt[get(1)]!=n) return INF;
    return res;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        cnt[i]=1;
    } //初始节点的父节点都为自己,连通块儿中点数都为1
    while(m--)
    {
        int x,y,z;
        cin>>x>>y>>z;
        if(x!=y) heap.push({z,{x,y}});
    }
    int t=kruskal();
    if(t==INF) puts("impossible");
    else printf("%d",t);
    return 0;
}

使用结构体存储边

#include<bits/stdc++.h>
using namespace std;
const int N = 200010,INF=0x3f3f3f3f;
int n,m,res,cnt;
int fa[N];
struct Edge
{
    int a,b,w;
}edges[N];
bool cmp(Edge x,Edge y) //定义规则,快排排序
{
    return x.w<y.w;
}
int get(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=get(fa[x]);
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=0;i<m;i++)
    {
        int x,y,z;
        cin>>x>>y>>z;
        edges[i]={x,y,z};
    }
    sort(edges,edges+m,cmp);
    for(int i=0;i<m;i++)
    {
        int a=edges[i].a,b=edges[i].b,w=edges[i].w;
        a=get(a),b=get(b); //找到a,b的根
        if(a!=b) //如果a,b不连通,加入连通块儿,cnt记录边数,边数+1 
        {
            fa[b]=a;
            res+=w;
            cnt++;
        }
    }
    if(cnt<n-1) puts("impossible"); //最小生成树边数=点数-1
    else cout<<res<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值