Kruskal算法求最小生成树(kruskal算法)

题目描述

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

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

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

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

输入格式

第一行包含两个整数 n 和 m。

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

输出格式

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

数据范围

1≤n≤10^5,
1≤m≤2∗10^5,
图中涉及边的边权的绝对值均不超过 1000。

输入样例:
4 5
1 2 1
1 3 2
1 4 3
2 3 2
3 4 4
输出样例:
6

算法思路:

将所有边按照权值的大小进行升序排序,然后从小到大一一判断。

如果这个边与之前选择的所有边不会组成回路,就选择这条边分;反之,舍去。

直到具有 n 个顶点的连通网筛选出来 n-1 条边为止。

筛选出来的边和所有的顶点构成此连通网的最小生成树。

判断是否会产生回路的方法为:使用并查集。

在初始状态下给各个个顶点在不同的集合中。

遍历过程的每条边,判断这两个顶点的是否在一个集合中。

如果边上的这两个顶点在一个集合中,说明两个顶点已经连通,这条边不要。如果不在一个集合中,则要这条边。

当我们遍历完了所有的边,得到的生成树的边数是n-1,则满足最小生成树。

我们使用结构体存储每条边。并且在从小到大排序边权重时使用sort函数,这就要用到<运算符重载。

示例代码:

#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e5+10,M=2e5+10,INF=0x3f3f3f3f;
int n,m;
int p[N];

//思路:按权值从小到大排序边,如果这条边与之前的边不会形成回路就选择这条边,直到找到最小生成树:遍历了图里的n个顶点和n-1条边
//我们使用并查集来判断是否产生回路,一开始各顶点在不同的集合中,遍历每条边,判断它的两个顶点是否在一个集合里面,如果在的话说明两个点之前连通了(通过其他的点),如果不在就选择这条边
struct Edge
{
    int a,b,w;
    
    bool operator< (const Edge &W)const  //按权重排序,括号中的const表示参数W对象不会被修改,最后的const表明调用函数对象不会被修改
    {
        return w<W.w;  //w是调用的对象,W.w是和它比较的,比如K<J,就是K.w<J.w
    }
}edges[M];

int find(int x) //并查集找祖宗节点
{
    if(x!=p[x]) p[x]=find(p[x]); //路径压缩+找祖宗节点
    return p[x];
}

int kruskal()
{
    sort(edges,edges+m);  //所有边按权重从小到大排序,sort函数会用到重载的<
    
    for(int i=1;i<=n;i++) //初始化并查集
    {
        p[i]=i;
    }
    int res=0,cnt=0;
    for(int i=0;i<m;i++) //遍历所有边
    {
        int a=edges[i].a, b=edges[i].b, w=edges[i].w;
        
        a=find(a),b=find(b);  //找到两个点的祖宗节点
        if(a!=b) //两个点不在同一个集合中,说明它们还没有连通,这条边可以加入到生成树里面
        {
            p[a]=b; //把这两个点加入集合,要注意是p[b]=a或者p[a]=b,不要写成a=p[b],因为后者是把p[b]赋值给a,无意义,我们要改变的是集合的祖宗节点也就是p[]
            res+=w; //这条边加入最小生成树
            cnt++;  //统计的边加一
        }
    }
    
    if(cnt!=n-1) return INF; //如果最小生成树的边不为n-1条边,则不能连通
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++) //输入所有边
    {
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        edges[i]={a,b,w};
    }
    
    int t=kruskal();
    if(t==INF) puts("impossible");
    else printf("%d\n",t);
    
    return 0;
}
特别注意:
if(a!=b) //两个点不在同一个集合中,说明它们还没有连通,这条边可以加入到生成树里面
        {
            p[a]=b; //把这两个点加入集合,要注意是p[b]=a或者p[a]=b,不要写成a=p[b],因为后者是把p[b]赋值给a,无意义,我们要改变的是集合的祖宗节点也就是p[]
            res+=w; //这条边加入最小生成树
            cnt++;  //统计的边加一
        }

这一段代码不要写成下面这样子:

if(a!=b) //两个点不在同一个集合中,说明它们还没有连通,这条边可以加入到生成树里面
        {
            b=p[a]; //把这两个点加入集合,要注意是p[b]=a或者p[a]=b,不要写成a=p[b],因为后者是把p[b]赋值给a,无意义,我们要改变的是集合的祖宗节点也就是p[]
            res+=w; //这条边加入最小生成树
            cnt++;  //统计的边加一
        }

我们要做的是合并集合,也就是a的祖宗节点的父节点指向b的祖宗节点,如果颠倒了顺序,那么p[a]就不会发生改变,也就是说a的祖宗节点的父节点依然是它自己,没有完成和b的祖宗节点的合并。会报错的。 

参考:AcWing 859. Kruskal算法求最小生成树---海绵宝宝来喽 - AcWing

相关推荐

  1. 算法基础之Kruskal算法生成

    2023-12-25 23:42:04       37 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-25 23:42:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-25 23:42:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-25 23:42:04       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-25 23:42:04       20 阅读

热门阅读

  1. docker搭建kali及安装oneforall

    2023-12-25 23:42:04       46 阅读
  2. 6_js数组常用函数进阶与String

    2023-12-25 23:42:04       39 阅读
  3. MultiValueMap

    2023-12-25 23:42:04       39 阅读
  4. 【大语言模型】Transformer原理以及运行机制

    2023-12-25 23:42:04       50 阅读
  5. arm day6

    2023-12-25 23:42:04       40 阅读
  6. 爬虫抓取链家二手房数据

    2023-12-25 23:42:04       34 阅读
  7. date工具类

    2023-12-25 23:42:04       31 阅读
  8. C语言中switch语句中的case后()

    2023-12-25 23:42:04       39 阅读
  9. [运维|shell] 编写shell脚本定期清理日志

    2023-12-25 23:42:04       39 阅读
  10. 第6章1-字符串及正则表达式 p63

    2023-12-25 23:42:04       32 阅读
  11. 避免约束系数过大的2种技巧

    2023-12-25 23:42:04       36 阅读
  12. ubuntu22.04上安装charles-proxy

    2023-12-25 23:42:04       34 阅读
  13. GO语言基础笔记(四):并发编程基础

    2023-12-25 23:42:04       33 阅读
  14. 第六章 卷:将磁盘挂载到容器

    2023-12-25 23:42:04       33 阅读