【算法模板】图论:最小生成树

最小生成树

给定一张边带权的无向图G=(V,E),n=|V|,m=|E|。由V中全部n个顶点和E中n-1条边构成的无向连通子图被称为G的一棵生成树。边的权值之和最小的生成树被称为无向图G的最小生成树(Minimum Spanning Tree, MST)。

给定一张无向图G=(V,E), n=|V|,m=|E|。从E中选出k

Prim算法

普利姆 (Prim) 算法是一种用于解决最小生成树问题的算法,又被称为加点法,适用于稠密图,其主要思路如下:

  • 选择任意一个顶点作为起始点,将其加入最小生成树中。

  • 从已选择的顶点集合中选取一个顶点,该顶点与未选择的顶点构成的边权重最小,并且该边的另一端顶点未被选择,将该顶点和边加入最小生成树中。

  • 重复上一步骤 ,直到最小生成树包含了图中的所有顶点。

Kruskal算法

克鲁斯卡尔 (Kruskal) 算法是一种用于解决最小生成树问题的算法,又被称为加边法,适用于稀疏图,其主要思路如下:

  • 建立并查集,每个点各自构成一个集合。

  • 把所有边按照权值从小到大排列,依次扫描每条边(x,y,z)。

  • 若x,y属于同一集合(连通),则忽略这条边,继续扫描下一条。

  • 否则,合并x,y所在的集合,并把z累加到答案中。

  • 所有边扫描完后,上一步处理的边就构成最小生成树。

例题及两种解法

点亮须弥【UUST】 - 蓝桥云课 (lanqiao.cn)

prim

#include
#include
#include
#include
using namespace std;
const int INF = 0x3f3f3f3f;
struct side { int to, len; };
int prim(const vector>& edges,int start=0) {
    const int n = edges.size();
    vector vis(n);
    priority_queue> q;
    q.push({ 0, 0 });
    int ans = 0;
    while (!q.empty()) {
        int nowp = q.top().second; 
        int d = -q.top().first;
        q.pop();
        if (vis[nowp])continue;
        ans += d;
        vis[nowp] = true;
        for (side s : edges[nowp]) {
            if (vis[s.to])continue;
            q.push({ -s.len,s.to });
        }
    }
    for (bool f : vis)if (!f)return -1;
    return ans;
}
int main() {
    int n, m; cin >> n >> m;
    vector> edges(n);
    while (m--) {
        int u, v, w;
        cin >> u >> v >> w;
        -- u, --v;
        edges[u].push_back({ v,w });
        edges[v].push_back({ u,w });
    }
    cout << prim(edges);
    return 0;
}

kruskal

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
using namespace std;
struct side { int u, v, w; };
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    vector edges(m);
    for (side& s : edges)scanf("%d%d%d", &s.u, &s.v, &s.w);
    vector fa(n + 1);
    iota(fa.begin(), fa.end(), 0);
    function find = [&](int x) {
        if (x == fa[x])return x;
        return fa[x]=find(fa[x]);//路径压缩
        };
    sort(edges.begin(), edges.end(), [](const side& s1, const side& s2) {
        return s1.w < s2.w; });
    int ans = 0;
    for (side s : edges) {
        int x = find(s.u);
        int y = find(s.v);
        if (x == y)continue;
        fa[x] = y;
        ans += s.w;
    }
    int flag = false;
    for (int i = 1; i <= n; i++) {
        if (i == fa[i] && !flag)flag = true;
        else if (i == fa[i] && flag) {
            cout << -1;
            return 0;
        }
    }
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(竞赛算法,图论,算法,数据结构)