Prim 最小生成树
💡 核心思想
Prim 算法从一个起始点出发,每次选择与当前生成树相连的边中权值最小的一条,将新顶点加入生成树。重复直到所有顶点都加入。适合稠密图,优先队列优化后适合稀疏图。
Prim 和 Dijkstra 非常相似——都是"从一个集合出发,贪心地扩展"。区别在于:Dijkstra 关注的是到源点的最短距离,Prim 关注的是到生成树的最近距离。代码结构几乎一样,把 dist[u] 的含义从"到源点距离"改成"到树的最短边权"就行。
🎯 直觉理解
想象你要用最少的电缆把所有房子连起来。你从某一栋房子开始,每次选择离已经连通的房子群最近的一栋新房,拉一条线过去。不断重复直到所有房子都连上了。
📝 算法流程
- 任选起点,标记已加入生成树
- 在所有"一端在树内、一端在树外"的边中选最小的
- 将新顶点和边加入生成树
- 重复 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-S | Prim/Kruskal模板 |