714 字
4 分钟
最小生成树 prim 和 kruskal 算法

一般对于最小生成树的题,都逃不掉prim和kruskual算法,这里简单讲一下两种算法吧

例题:P3366

1.Kruskual算法#

又被称为克鲁斯卡尔算法,一般用于处理点多边少的的最小生成树

其原理是:

类似于贪心算法,通过利用并查集来维护这个图的连通性,先将边按照权的大小开始排序(升序),然后依次开始检查边的两个端点在未添加这条边时是否已经连通,若没有连通,则将此边添加到最小生成树的图中,否则则跳过这条边。

依次执行上述动作,直到检查了所有的端点,便可以得出一个最小生成树

/*
 * @Author: Mr.Sen
 * @LastEditTime: 2020-07-13 17:32:18
 * @Website: https://grimoire.cn
 * @Mr.Sen All rights reserved
 * 加入秩和压缩优化
 */ 

#include <bits/stdc++.h>
using namespace std;

struct node {
    int u,v,w;
} lst[200005];

int parent[200005];
int ranks[200005];

bool cmp(node s1, node s2) {
    return s1.w < s2.w;
}

int find(int x) {
    return x == parent[x] ? x : (parent[x] = find(parent[x]));
}

void toUnion(int x1, int x2) {
    int p1 = find(x1);
    int p2 = find(x2);

    if (ranks[p1] > ranks[p2]) {
        parent[p1] = p2;
    } else {
        parent[p2] = p1;
        
        if (ranks[p1] == ranks[p2]) {
            ranks[p2]++;
        }
    }
}

int main() {
    int n,m;
    cin>>n>>m;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", &lst[i].u, &lst[i].v, &lst[i].w);
    }
    sort(lst + 1,lst + m + 1,cmp);
	// 读入数据并将数据按照边的权进行排序
    
    for (int i = 1; i <= n; i++) {
        parent[i] = i;
        // 初始化
    }

    int sum = 0, tot = 0, flag =0;

    for (int i = 1; i <= m; i++) {
        int u = lst[i].u;
        int v = lst[i].v;
        int w = lst[i].w;

        if (find(u) != find(v)) {
            // 依次检查两点是否连通,若不连通则将其连通,并加入所需代价
            toUnion(u, v);
            sum += w;
            tot++;
        }
        
        if (tot == n - 1 ) {
            // 当执行此程序时,生成了正确的最小生成树
            flag = 1;
            break;
        }
    }
    if (flag == 1) {
        cout<<sum<<endl;
        // 输出总的权
    } else {
        cout<<-1<<endl;
        // 所给数据不能生成最小生成树
    }
    
    return 0;
}

2.Prim算法#

该算法请看这篇图文 (日常偷懒划水 )

我给一份代码吧

/*
 * @Author: Mr.Sen
 * @LastEditTime: 2020-07-14 22:37:35
 * @Website: https://grimoire.cn
 * @Mr.Sen All rights reserved
 */ 
#include <bits/stdc++.h>
using namespace std;

const int M = 5001, INF = 999999999;
int n, e;
int value[M][M];
int lowCost[M];

void prim(int s) {
    int i, j, count = 0, minn, k;
    for (i = 1; i <= n; i++) {
        lowCost[i] = value[s][i];
    }
    lowCost[s] = 0;
    for (i = 1; i < n; i++) {
        minn = INF;
        for (j = 1; j <= n; j++) {
            if (lowCost[j] && lowCost[j] < minn) {
                minn = lowCost[j];
                k = j;
            }
        }
        lowCost[k] = 0;
        count += minn;
        for (j = 1; j <= n; j++) {
            if (value[k][j] < lowCost[j]) {
                lowCost[j] = value[k][j];
            }
        }
    }
    printf("%d\n", count);
}

int main() {
    int x, y, weight;
    for (int i = 0; i <= M; i++) {
        for (int j = 0; j <= M; j++) {
            value[i][j] = INF;
        }
    }
    scanf("%d%d", &n, &e);
    for (int i = 1; i <= e; i++) {
        cin >> x >> y >> weight;
        if (weight < value[x][y]) {
            value[y][x] = value[x][y] = weight;
        }
    }
    prim(n);
    return 0;
}
最小生成树 prim 和 kruskal 算法
https://grimoire.cn/posts/algorithm/kruskal/
作者
北城
发布于
2024-09-09
许可协议
CC BY-NC-SA 4.0