1. 程式人生 > >道路升級(最小生成樹KRUSKAL)

道路升級(最小生成樹KRUSKAL)

道路升級

問題描述

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

資料範圍

對於 20% 的資料,保證 n≤5,m≤10。

對於 60% 的資料,保證 n≤1,000,m≤5,000。

對於 100% 的資料,保證 n≤200,000,m≤400,000。

時間限制:10 sec

空間限制:256 MB

提示

[提示1:真的可能有多種方案嗎?]

[提示2:k 是否一定為 n-1 呢?(也就是說,選出的道路是否恰好構成了一棵樹?)]

[提示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;
}