视频加载失败

最小生成树

990 字
5 分钟
最小生成树

定义#

最小生成树是指在一张无向图中,选取一些边,使其满足以下条件:

  1. 选择的边能够使图中的每一个节点直接或间接相连
  2. 在满足条件 1 的前提下,使得所有边的和尽可能小.(即获得连通所有点的最小代价)

[!node]- 题目:P3366 最小生成树

如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz

输入格式#

第一行包含两个整数 N,MN,M,表示该图共有 NN 个结点和 MM 条无向边.

接下来 MM 行每行包含三个整数 Xi,Yi,ZiX_i,Y_i,Z_i,表示有一条长度为 ZiZ_i 的无向边连接结点 Xi,YiX_i,Y_i

输出格式#

如果该图连通,则输出一个整数表示最小生成树的各边的长度之和.如果该图不连通则输出 orz

数据范围#

对于 100%100\% 的数据:1N50001\le N\le 50001M2×1051\le M\le 2\times 10^51Zi1041\le Z_i \le 10^4

kruskal 算法#

kruskal 算法的本质是贪心,主要分为以下几步

  1. 先将所有边按权值从小到大排序.
  2. 依次选择权值最小的边,如果这条边连接的两个端点不在同一个连通块(即不形成环),就将这条边加入生成树.
  3. 重复上述过程,直到选出 n-1 条边(n 为节点数),或者所有边都被处理完.
  4. 如果仍旧有点没有被连通,那么原图就不是连通图

判断两个边是否连通,常使用并查集.

时间复杂度:O(m)O(m)

标程——kruskal#

#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 100;
const int M = 2e5 + 100;
int n, m;
int f[N];
// 定义一条边
struct Edge
{
// u表示边的起点
// v表示边的终点
// w表示边的权值
int u, v, w;
friend bool operator<(Edge a, Edge b)
{
return a.w < b.w;
}
} e[M];
// 并查集模板
class DSU
{
public:
// 初始化并查集
void build(int size)
{
for (int i = 1; i <= size; i++)
{
f[i] = i;
}
}
// 找到节点t的最顶端的父节点
int find(int t)
{
if (f[t] != t)
f[t] = find(f[t]);
return f[t];
}
// 将节点a和节点b所在集合合并
void merge(int a, int b)
{
int x = find(a), y = find(b);
if (x != y)
f[x] = y;
}
// 查询a和b是否处于同一集合
bool query(int a, int b)
{
return find(a) == find(b);
}
} dsu;
void kruskal()
{
// 进行排序
sort(e + 1, e + m + 1);
int ans = 0;
// 依次选取每一条边,尝试添加
for (int i = 1; i <= m; i++)
{
int u = e[i].u, v = e[i].v;
if (!dsu.query(u, v))
{
ans += e[i].w;
dsu.merge(u, v);
}
}
// 判断是否连通
for (int i = 1; i <= n; i++)
{
if (!dsu.query(1, i))
{
cout << "orz" << endl;
return;
}
}
cout << ans << endl;
return;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
cin >> e[i].u >> e[i].v >> e[i].w;
}
dsu.build(n);
kruskal();
}

prim 算法#

Prim 算法也是一种贪心.

核心思想:

  • 从任意一个点出发,每次选择一条连接已选点集和未选点集、权值最小的边,将新点加入生成树.
  • 重复 n-1 次,直到所有点都被包含.

常用优先队列(堆)优化,时间复杂度 O(mlogn)O(m\log n)

适合稠密图,代码实现类似 Dijkstra,但 Prim 关注的是“连通所有点的最小代价”,不是最短路.

标程——prim#

#include <bits/stdc++.h>
using namespace std;
const int N = 5e3 + 100;
const int M = 2e5 + 100;
int n, m;
struct Edge
{
int to, w;
friend bool operator<(Edge a, Edge b)
{
return a.w > b.w;
}
};
vector<Edge> e[N];
bool mark[N];
void prim()
{
int ans = 0;
priority_queue<Edge> q;
q.push({1, 0});
while (!q.empty())
{
int v = q.top().to, w = q.top().w;
q.pop();
if (mark[v])
continue;
mark[v] = true;
ans += w;
for (int i = 0; i < e[v].size(); i++)
{
q.push(e[v][i]);
}
}
for (int i = 1; i <= n; i++)
{
if (!mark[i])
{
cout << "orz";
return;
}
}
cout << ans;
return;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
e[u].push_back({v, w});
e[v].push_back({u, w});
}
prim();
}

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

最小生成树
https://blog.jerrylab.top/posts/graph/mst/
作者
Jerry
发布于
2026-03-02
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Jerry
Hello, I'm Jerry.
公告
欢迎来到我的博客!这是一则示例公告。
分类
标签
最新动态
站点统计
文章
80
分类
3
标签
35
总字数
89,198
运行时长
0
最后活动
0 天前
站点信息
构建平台
Cloudflare Pages
博客版本
Firefly v6.16.5
文章许可
CC BY-NC-SA 4.0