P3366 【模板】最小生成树

GESP8 冲刺课堂页:先建模,再压缩状态,最后读代码。

一、先回答问题

结论

把边按权值从小到大排序,能连接两个不同连通块就选。

容易误入的路

只选每个点最短的一条边,可能形成环或不能连通所有点。

二、核心观察

建模与压缩

并查集维护当前连通块;cnt 记录已选边数。

为什么不丢答案:Kruskal 的割性质保证当前最小且不成环的边可以安全加入。

当前边 连通块 已选 cnt

三、互动演示

步骤1
关键状态 A-
关键状态 B-
答案/目标-
复杂度看表
课堂动作手推

四、自己选答案

问题 1

为什么要按边权排序?

问题 2

cnt 不到 n-1 怎么办?

五、手推结果

步骤动作
排序按 w 升序
扫描不同集合才选
结束cnt==n-1 才有 MST

课堂要求:每题先说清对象、关系、保留的信息、压缩掉的信息,再写代码。

六、C++14 参考代码

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

struct Edge {
    int u, v, w;
    bool operator<(const Edge &other) const {
        return w < other.w;
    }
};

const int N = 5005;

int fa[N];

int find(int x) {
    if (fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}

bool unite(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y) return false;
    fa[x] = y;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;

    vector<Edge> edges(m);
    for (int i = 0; i < m; i++) {
        cin >> edges[i].u >> edges[i].v >> edges[i].w;
    }

    sort(edges.begin(), edges.end());

    for (int i = 1; i <= n; i++) fa[i] = i;

    long long ans = 0;
    int cnt = 0;
    for (const Edge &e : edges) {
        if (unite(e.u, e.v)) {
            ans += e.w;
            cnt++;
            if (cnt == n - 1) break;
        }
    }

    if (cnt == n - 1) cout << ans << '\n';
    else cout << "orz\n";

    return 0;
}