城市道路建设方案优化:最小生成树问题

假设你是城市规划师,现在需要在 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. 初始化并查集:将每个村庄初始化为一个独立的集合。
  2. 排序:将所有边按照边权从小到大排序(本题中边权均为 1,所以排序可以省略)。
  3. 依次加入边:遍历每条边,对于当前边 (a, b):
    • 如果 a 和 b 已经在同一个集合中,说明这条边是冗余的,不需要加入。
    • 如果 a 和 b 不在同一个集合中,则将 a 和 b 所在的集合合并,并需要新建一条边。
  4. 计算需要新建边的数量:每当合并两个集合时,需要新建一条边,统计每次新建边的数量,并将结果输出。

算法复杂度分析:

  • 并查集操作的时间复杂度为 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 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录