1. 程式人生 > >算法訓練營:道路升級

算法訓練營:道路升級

算法訓練 有一個 之間 ini color span 代碼 tor 結點

問題描述

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 行每行一個整數,從小到大輸出所有選出的道路的編號。

輸入樣例

5 3
1
3
5
10
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:這道題和最小生成樹有什麽關系呢?]

代碼實現:

 1 #include <iostream>
 2 #include <vector>
 3 using namespace std;
 4 
 5 // ================= 代碼實現開始 =================
6 7 const int N = 500005 ; 8 9 class UnionSet{ 10 public: 11 int f[N] ; 12 //初始化 13 //n :節點數量 14 void init( int n ) 15 { 16 for( int i = 1 ; i <= n ; i++ ) 17 f[i] = i ; 18 } 19 int find( int x ) 20 { 21 return f[x] == x ? x : f[x] = find( f[x] ) ; 22 } 23 //將x節點和y節點所在集合合並 24 // x:一個節點 25 // y : 一個結點 26 //返回值: 返回true表示成功合並,返回false表示已經在一個集合裏了 27 bool merge( int x , int y ) 28 { 29 int setX = find(x) ; 30 int setY = find(y) ; 31 if( setX != setY ) 32 { 33 f[setX] = setY ; 34 return true ; 35 } 36 return false ; 37 } 38 }us; 39 40 // 給定一個n個點m條邊的無向圖,第i條邊邊權為i,求所有需要升級的邊 41 // n:點->城市 42 // m:邊->道路 43 // U:大小為m的數組,表示m條邊的其中一個端點 44 // V:大小為m的數組,表示m條邊的另一個端點 45 // 返回值:所有需要升級的邊,從小到大排列;第一小問的答案自然即為返回值的size,所以你不必考慮如何返回size 46 47 vector<int>getAnswer(int n, int m, vector<int> U, vector<int> V) 48 { 49 vector<int> ans; 50 us.init(n); 51 //構造最大生成樹,找權值最大的邊 52 for( int i = m - 1 ; i >= 0 ; --i ) 53 if( us.merge( U[i] , V[i] ) ) 54 ans.push_back( i + 1 ) ; //加入邊,從1開始到m 55 reverse(ans.begin() , ans.end()); //原來ans試從小到大排列的,現將ans數組從大到小排列 56 return ans; 57 } 58 59 int main() 60 { 61 int n, m; 62 scanf("%d%d", &n, &m); 63 vector<int> U, V; 64 for (int i = 0; i < m; ++i) { 65 int u, v; 66 scanf("%d%d", &u, &v); 67 U.push_back(u); 68 V.push_back(v); 69 } 70 vector<int> ans = getAnswer(n, m, U, V); 71 printf("%d\n", int(ans.size())); 72 for (int i = 0; i < int(ans.size()); ++i) 73 printf("%d\n", ans[i]); 74 return 0; 75 }

算法訓練營:道路升級