1. 程式人生 > >鄧俊輝 演算法訓練營練習 道路升級

鄧俊輝 演算法訓練營練習 道路升級

道路升級 問題描述 Z國有 n 個城市和 m 條雙向道路,每條道路連線了兩個不同的城市,保證所有城市之間都可以通過這些道路互達。每條道路都有一個載重量限制, 這限制了通過這條道路的貨車最大的載重量。道路的編號從 1 至 m 。巧合的是,所有道路的載重量限制恰好都與其編號相同。

現在,要挑選出若干條道路,將它們升級成高速公路,並滿足如下要求:

所有城市之間都可以通過高速公路互達。 對於任意兩個城市 u,v 和足夠聰明的貨車司機:只經過高速公路從 u 到達 v 能夠裝載貨物的最大重量,與經過任意道路從 u 到達 v 能夠裝載貨物的最大重量相等。 (足夠聰明的司機只關注載重量,並不在意繞路) 在上面的前提下,要求選出的道路數目儘可能少。

求需要挑選出哪些道路升級成高速公路(如果有多種方案請任意輸出一種)。

輸入 第一行 2 個用空格隔開的整數 n,m ,分別表示城市數目、道路數目。

第 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;

//F[N] 用來記錄父節點
//r[N] 用來記錄此集合的元素個數(即:連通道路的數目)
int F[N];
int r[N];
//查詢最上面的父節點
int find(int x){
    return F[x] == x?x:find(F[x]);
}
// JL用來記錄升級道路的編號
vector<int> JL;
void getAnswer(int m,vector<int> A,vector<int> B){
    //一開始父節點都是自己
    //連通的道路為0
    for(int i = 0;i < m;i++){
        F[i] = i;
        r[i] = 0;
    }
    //從載重量大的道路開始連通
    for(int i = m - 1;i >= 0;i--){
        //尋找A[i] 與 B[i] 的父節點
        int setA = find(A[i]);
        int setB = find(B[i]);

        //如果父節點不同說明兩個城市還沒有接通
        if(setA != setB)
            //此時尋找 道路數多的 一方的父親節點作為 父親節點
            if(r[setA] >= r[setB]){
                F[setB] = setA;
                r[setA] += r[setB];//道路數相加
                JL.push_back(i + 1);//記錄升級了第幾條道路
        }
    }
}
int main(int argc, char const *argv[]) {
    //n個城市 m條道路
    int n,m;
    scanf("%d%d",&n,&m);

    //A[i] - B[i]代表 兩條連通的通路
    vector<int> A;
    vector<int> B;
    for(int i = 0;i < m;i++){
        int ai,bi;
        scanf("%d%d",&ai,&bi);
        A.push_back(ai);
        B.push_back(bi);
    }
    getAnswer(m,A,B);

    //輸出升級到高速公路的的數目
    cout << JL.size() << "\n";
    //輸出每條升級到高速公路的編號
    while(!JL.empty()){
        cout << JL.back() << "\n";
        JL.pop_back();
    }
    return 0;
}