Prim 最小生成树CSP-S

目录

💡 核心思想

Prim 算法从一个起始点出发,每次选择与当前生成树相连的边中权值最小的一条,将新顶点加入生成树。重复直到所有顶点都加入。适合稠密图,优先队列优化后适合稀疏图。

Prim 和 Dijkstra 非常相似——都是"从一个集合出发,贪心地扩展"。区别在于:Dijkstra 关注的是到源点的最短距离,Prim 关注的是到生成树的最近距离。代码结构几乎一样,把 dist[u] 的含义从"到源点距离"改成"到树的最短边权"就行。

🎯 直觉理解

想象你要用最少的电缆把所有房子连起来。你从某一栋房子开始,每次选择离已经连通的房子群最近的一栋新房,拉一条线过去。不断重复直到所有房子都连上了。

📝 算法流程

  1. 任选起点,标记已加入生成树
  2. 在所有"一端在树内、一端在树外"的边中选最小的
  3. 将新顶点和边加入生成树
  4. 重复 n-1 次

$$\text{MST总权值} = \sum_{(u,v) \in T} w(u,v)$$

📊 复杂度分析

指标复杂度
时间优先队列 $O((V+E)\log V)$,朴素 $O(V^2)$
空间$O(V+E)$

💻 参考实现(C++)

C++ (C++17)
#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m; cin >> n >> m;
    vector<vector<pair<int,int>>> adj(n+1);
    for (int i = 0; i < m; i++) {
        int u, v, w; cin >> u >> v >> w;
        adj[u].push_back({v, w});
        adj[v].push_back({u, w});
    }
    long long total = 0;
    vector<int> minW(n+1, INT_MAX);
    vector<bool> vis(n+1, false);
    priority_queue<pair<int,int>, vector<pair<int,int>>, greater<>> pq;
    minW[1] = 0; pq.push({0, 1});
    while (!pq.empty()) {
        auto [w, u] = pq.top(); pq.pop();
        if (vis[u]) continue;
        vis[u] = true; total += w;
        for (auto [v, c] : adj[u]) {
            if (!vis[v] && c < minW[v]) {
                minW[v] = c;
                pq.push({c, v});
            }
        }
    }
    cout << total << endl;
    return 0;
}

⚠️ 常见坑点

图不连通时无法生成MST——要判断连通性

优先队列中的过时记录要跳过

无向图的边要加两次

总权值可能超过 int 范围

📚 相关题目

题目来源难度备注
P3366 最小生成树洛谷CSP-SPrim/Kruskal模板