清华大学算法训练营——程序七:道路升级

道路升级(Kruscal)

问题描述

Z国有 n 个城市和 m 条双向道路,每条道路连接了两个不同的城市,保证所有城市之间都可以通过这些道路互达。每条道路都有一个载重量限制,这限制了通过这条道路的货车最大的载重量。道路的编号从 1 至 m 。巧合的是,所有道路的载重量限制恰好都与其编号相同。

现在,要挑选出若干条道路,将它们升级成高速公路,并满足如下要求:

  • 所有城市之间都可以通过高速公路互达。
  • 对于任意两个城市 u,v 和足够聪明的货车司机:只经过高速公路从 u 到达 v 能够装载货物的最大重量,与经过任意道路从 u 到达 v 能够装载货物的最大重量相等。(足够聪明的司机只关注载重量,并不在意绕路)

在上面的前提下,要求选出的道路数目尽可能少

求需要挑选出哪些道路升级成高速公路(如果有多种方案请任意输出一种)。

简要题意

n个点和m条边,第i条边的边权为i,挑出一些边,满足:

  1. 任意两个点都是连通的
  2. 在所挑出边形成的图中,对于任意两个点,设两点之间所有路径所含的边的最大边权为maxEdge,则在原图中,对于任意两个点,两点之间所有路径所含的边的最大边权不大于maxEdge。
  3. 满足前两个条件的情况下,边最小。

输入

第一行 2 个用空格隔开的整数 n,m ,分别表示城市数目、道路数目。 ( 1 < n < 2 10 5 ) ( 1 < m < 4 10 5 )

第 2 行到第 m+1 行,每行 2 个用空格隔开的整数 u,v 描述一条从 u 到 v 的双向道路,第 i+1 行的道路的编号为 i 。

注意:数据只保证不存在连接的城市相同的道路(自环),并不保证不存在两条完全相同的边(重边)

输出

第一行一个整数 k ,表示升级成高速公路的道路数。

接下来 k 行每行一个整数,从小到大输出所有选出的道路的编号。

输入样例

3 3
1 2
2 3
1 3

输出样例

2
2
3
#include <bits/stdc++.h>
using namespace std;
const int N = 500005;
//并查集
class UnionSet{
  public:
    int f[N];
    //初始化父节点   n:节点数量   开始每个节点的父节点是它自己本身
    void init(int n){
        for(int i=1; i<=n; i++)
            f[i] = i;
    }
    //查找节点x所在集合的根(路径压缩)
    //x:节点x
    //返回值:根
    int find(int x){
        return f[x] == x?x:f[x]=find(f[x]);
    }
    //将x节点与y节点所在集合合并
    //x:一个节点
    //y:一个节点
    //返回值:返回true表示成功合并,返回false表示已经在一个集合里了
    bool merge(int x,int y){
        int setX = find(x);
        int setY = find(y);
        if(setX != setY){
            f[setX] = setY;
            return true;
        }
        return false;
    }
}us;
// 给定一个n个点m条边的无向图,第i条边边权为i,求所有需要升级的边
// n:n个点
// m:m条边
// U:大小为m的数组,表示m条边的其中一个端点
// V:大小为m的数组,表示m条边的另一个端点
// 返回值:所有需要升级的边,从小到大排列;第一小问的答案自然即为返回值的size,所以你不必考虑如何返回size
vector<int> getAnswer(int n, int m, vector<int> U, vector<int> V) {
    vector<int> ans;
    us.init(n);
    //构造最大生成树,找权值最大的边
    for(int i=m-1; i>=0; --i)
        if(us.merge(U[i],V[i]))
            ans.push_back(i+1);//加入边,从1开始的
    reverse(ans.begin(),ans.end());
    return ans;
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    vector<int> U, V;
    for (int i = 0; i < m; ++i) {
        int u, v;
        scanf("%d%d", &u, &v);
        U.push_back(u);
        V.push_back(v);
    }
    vector<int> ans = getAnswer(n, m, U, V);
    printf("%d\n", int(ans.size()));
    for (int i = 0; i < int(ans.size()); ++i)
        printf("%d\n", ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhaohaibo_/article/details/81455832