
[洛谷] P3366-【模板】最小生成树
知识点
最小生成树
表示一个无向连通图中边权和最小的生成树
并查集
可以实现:
-
合并:合并两个元素所属集合(合并对应的树)
-
查询:查询某个元素所属集合(查询对应的树的根节点),这可以用于判断两个元素是否属于同一集合
题目
P3366 【模板】最小生成树
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出
orz
。输入格式
第一行包含两个整数 $N,M$,表示该图共有 $N$ 个结点和 $M$ 条无向边。
接下来 $M$ 行每行包含三个整数 $X_i,Y_i,Z_i$,表示有一条长度为 $Z_i$ 的无向边连接结点 $X_i,Y_i$。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出
orz
。输入输出样例 #1
输入 #1
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
输出 #1
7
说明 / 提示
数据规模:
对于 $20%$ 的数据,$N\le 5$,$M\le 20$。
对于 $40%$ 的数据,$N\le 50$,$M\le 2500$。
对于 $70%$ 的数据,$N\le 500$,$M\le 10^4$。
对于 $100%$ 的数据:$1\le N\le 5000$,$1\le M\le 2\times 10^5$,$1\le Z_i \le 10^4$。
样例解释:
所以最小生成树的总边权为 $2+2+3=7$。
题解
Prim 算法
Prim 算法的思想是从一个顶点开始,不断扩大生成树,每次选择与当前生成树相连的权值最小的边来加入树中。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int INF = INT_MAX;
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 });
}
vector<int>d(n + 1, INF);
vector<bool>vis(n + 1, 0);
d[1] = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;// 优先队列
pq.push({ 0,1 });
int res = 0, cnt = 0;// res权值,cnt边数
while (!pq.empty()) {
auto dd = pq.top().first;
auto u = pq.top().second;
pq.pop();
if (vis[u])continue;// 若访问过就跳过
vis[u] = 1;// 标记访问
res += dd;// 增加权值
cnt++;// 增加边数
for (auto ed : adj[u]) {
int v = ed.first;// 邻居
int w = ed.second;// 该顶点和此邻居的边权
if (!vis[v] && w < d[v]);
d[v] = w;// 更新最短距离
pq.push({ w,v });// 入队
}
}
if (cnt == n)cout << res;
else cout << "orz";
return 0;
}
剩下两种突然不想写了…… 实则因为以前没写过
反正 Prim 算法就够用了,剩下两个还需要并查集
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果