最小生成树问题:城市道路建设方案优化
城市道路建设方案优化:最小生成树问题
假设你是城市规划师,现在需要在 n 个村庄之间修建 m 条道路,每条道路都是双向的。你的目标是:在修建道路的过程中,尽量减少新建道路的数量,确保所有村庄之间都能互相到达。
问题描述:
给定 n 个村庄和 m 条已经修建好的道路,你需要计算在每次新增一条道路后,至少还需要新建多少条道路才能使所有村庄互相可达。
输入格式:
- 第一行包含两个整数 n 和 m,分别表示村庄数量和已建道路数量,其中 0 ≤ n, m ≤ 10^5。
- 接下来 m 行,每行两个整数 a 和 b,表示已建好一条连接第 a 个村庄和第 b 个村庄的道路。1 ≤ a, b ≤ n,a 和 b 可能相同。
输出格式:
一共 m 行,第 i 行输出已经修建了前 i 条路时,最少还需要修建几条路才能使所有村庄互相可达。
算法:并查集 + 贪心
- 初始化并查集:将每个村庄初始化为一个独立的集合。
- 排序:将所有边按照边权从小到大排序(本题中边权均为 1,所以排序可以省略)。
- 依次加入边:遍历每条边,对于当前边 (a, b):
- 如果 a 和 b 已经在同一个集合中,说明这条边是冗余的,不需要加入。
- 如果 a 和 b 不在同一个集合中,则将 a 和 b 所在的集合合并,并需要新建一条边。
- 计算需要新建边的数量:每当合并两个集合时,需要新建一条边,统计每次新建边的数量,并将结果输出。
算法复杂度分析:
- 并查集操作的时间复杂度为 O(α(n)),其中 α(n) 是阿克曼函数的反函数,增长速度极慢,可以近似看作常数。
- 排序的时间复杂度为 O(m log m)。
- 遍历所有边的总时间复杂度为 O(m)。
因此,整个算法的时间复杂度为 O(m log m)。
C++ 代码:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1e5 + 5;
int fa[MAXN];
// 初始化并查集
void init(int n) {
for (int i = 1; i <= n; ++i) {
fa[i] = i;
}
}
// 查找祖先节点
int find(int x) {
if (fa[x] != x) {
fa[x] = find(fa[x]);
}
return fa[x];
}
// 合并两个集合
void merge(int x, int y) {
int fx = find(x);
int fy = find(y);
if (fx != fy) {
fa[fx] = fy;
}
}
int main() {
int n, m;
cin >> n >> m;
init(n);
vector<pair<int, int>> edges;
for (int i = 0; i < m; ++i) {
int a, b;
cin >> a >> b;
edges.push_back({a, b});
}
int need_edges = 0;
for (int i = 0; i < m; ++i) {
int a = edges[i].first;
int b = edges[i].second;
if (find(a) != find(b)) {
merge(a, b);
need_edges++;
}
cout << need_edges << endl;
}
return 0;
}
原文地址: https://www.cveoy.top/t/topic/m6zB 著作权归作者所有。请勿转载和采集!