题目传送门

一、Kruskal算法

1、基本思路:

(1) 将所有边按权重从小到大排序

(2) 枚举每条边 \(a \sim b\) ,权重是\(c\)

    if a,b不在一个集合中 :
        将这条边加入集合中
    结束  

\(prim\)算法的区别

  • 克鲁斯卡尔算法的基本思想是以边为主导地位,普利姆算法是以点为主导的地位的。
  • \(prim\)算法适合稠密图,\(kruskal\)算法适合稀疏图。理由也挺简单的,\(kruskal\)是按边存的,边少就合适,边多就不适合。稀疏图当然边少,稠密图是点少,但边多,边可能达到节点数的平方,即每个节点都与其它节点有边。

3、算法模拟

假如有以下几个城市,之间都有相连的道路:

AcWing 859. Kruskal算法求最小生成树

根据\(kruskal\)的原理,我们需要对边权\(dis\)进行排序,每次找出最小的边。
排序后,最小的边自然是第\(8\)条边,于是\(4\)\(6\)相连。

AcWing 859. Kruskal算法求最小生成树
遍历继续,第二小的边是$1$号,$1$和$2$联通。
AcWing 859. Kruskal算法求最小生成树
再后来是边$3$连接$1$,$4$。
AcWing 859. Kruskal算法求最小生成树
$dis$也是$14$的还有边$5$,它连接$3$,$4$。
AcWing 859. Kruskal算法求最小生成树

其次是\(dis\)\(15\)的边\(4\),但是\(2\)\(4\)已经相连了,\(pass\)

然后是\(dis\)\(16\)的两条边(边\(2\)和边\(9\)),边\(2\)连接\(1\)\(3\),边\(9\)连接\(3\)\(6\),它们都已经间接相连,\(pass\)

再然后就是\(dis\)\(22\)的边\(10\),它连接\(5\)\(6\)\(5\)还没有加入组织,所以使用这边。继续,发现此时已经连接了\(n-1\)条边,结束,最后图示如下:

AcWing 859. Kruskal算法求最小生成树

本题与 https://www.acwing.com/problem/content/839/ 是姊妹题,其实\(Kruskal\)算法就是一个并查集的应用。

不像\(Prim\)算法,不用考虑边界,考虑循环\(N\)次啊,计算最小值啊,还要用堆进行优化啊,这个就是一个并查集,思路简单。

4、需要回答的问题

Q:只需要简单结构体即可,不需要邻接表或者邻接矩阵来存,为什么呢?

A:之所以使用邻接表或邻接矩阵,其实说白了,是按点存的,记录\(A\)点和\(B\)点的关系。用结构体存储,其实是按边存的,就是题目说有一条\(A-B\)的边(权为\(C\)),我们就存了一个\(A-B\)权为\(C\)的边。
按点存麻烦(邻接表或邻接矩阵),按边存(结构体数组)简单。

二、完整代码

#include <bits/stdc++.h>

using namespace std;

const int N = 100010;
const int INF = 0x3f3f3f3f;

int n, m;
int p[N];
int res;
int cnt;

//只需要简单结构体即可,不需要链表
struct Edge {
    int a, b, w;
    // 需要重载<号,利用w进行排序
    bool operator<(const Edge &W) const {
        return w < W.w;
    }
} edges[N << 1];

//并查集模板
int find(int x) {
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int kruskal() {
    // 0、初始化并查集
    for (int i = 1; i <= n; i++) p[i] = i;
    //1、按边的权重排序
    sort(edges + 1, edges + 1 + m);

    //2、从小到大枚举每一条边[有些边是要放弃滴]
    for (int i = 1; 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;   //a认b为祖宗,合并两个集合
            res += w;   //最小生成树中所有树边的权重之和
            cnt++;      //加入了多少条边
        }
    }
    //如果加入的边数小于n-1,说明不连通
    if (cnt < n - 1) return INF;
    //返回所有树边的长度之和
    return res;
}


int main() {
    //读入优化
    ios::sync_with_stdio(false);
    cin >> n >> m;

    for (int i = 1; i <= m; i++) {
        int a, b, w;
        cin >> a >> b >> w;
        edges[i] = {a, b, w};
    }
    //调用克鲁斯卡尔算法
    int t = kruskal();

    if (t == INF) puts("impossible");
    else printf("%d\n", t);

    return 0;
}

相关文章:

  • 2021-11-21
  • 2021-08-09
  • 2021-10-04
  • 2021-05-23
  • 2021-09-30
  • 2021-10-26
  • 2021-09-25
  • 2022-03-02
猜你喜欢
  • 2022-01-14
  • 2021-09-05
  • 2021-12-27
相关资源
相似解决方案