CCF-CSP 201812-4 數據中心(Kruskal最小生成樹)

Kruskal最小生成樹ios

  思路:題目要求的傳輸圖是樹結構,所以是生成樹問題。另外一方面,咱們將會看到最優解就是最小生成樹。算法

  題中「傳輸圖」的傳輸時間實質上爲連通路徑上的最大傳輸時間。咱們發現當生成樹上傳輸時間總和最小時,其中的最大傳輸時間也最小(傳輸時間不爲負值,因此最大傳輸時間<傳輸時間總和,再放縮一下即得)。 所以所求最優解就是是原圖的最小生成樹。緩存

  Kruskal算法實際上是一種貪心算法,每次都選取權值小的邊去構造生成樹,使得最終的生成樹邊權總和最小。若新加入的邊會產生環,使得結果再也不知足樹的性質,則應當丟棄該邊,能夠經過判斷邊兩端的頂點是否在同一連通份量中實現這種判斷,通常採用並查集完成。異步

  根據生成樹性質,當恰有N-1條邊加入到生成樹中時,生成樹構造完畢。(N爲頂點個數)。採用並查集後,只剩下1個集合時樹構造完成。優化

 

AC代碼:spa

  1 /*
  2  * 1. 若定義變量名rank,則CSP報編譯出錯,改成rank_1後經過。緣由未知!
  3  * 
  4  * 2. 題中「樹結構傳輸圖」的傳輸時間實質上爲連通路徑上最大的傳輸時間。目標是求使總傳輸時間最小的樹: 
  5  *        當連通路徑上傳輸時間總和最小時,其中的最大傳輸時間也最小(傳輸時間不爲負值)。 
  6  *        所以所求「最優結構」是原圖的最小生成樹。 
  7  * 3. 使用kruskal算法時,基於題目所給出的數據規模,應對並查集採用簡單的按秩合併優化或者路徑壓縮。 
  8  *        秩增長的惟一條件是:兩個集合對應子樹的秩相等。由於合併集合時,
  9  *        老是將秩小的樹合併到秩大的樹上,總秩不會發生任何改變,除非兩樹等秩時,才使總秩增長1。 
 10  *        本例實測,單純使用簡單按秩合併優化後,執行時間爲原來的 1/40!
 11  * 4. 還可考慮路徑壓縮算法,即每次father()搜索時,將被查找過的節點的parent都指向它們的直接根節點。
 12  *        遞歸實現:Line 45。
 13  *        本例實測,單純使用路徑壓縮優化後,執行時間爲原來的 1/40!  
 14  */
 15  
 16 #include <iostream>
 17 #include <algorithm>
 18 
 19 #include <fstream>
 20 
 21 struct Edge {
 22     int u, v;
 23     int t;
 24     
 25     inline bool operator < (const Edge &e) const {
 26         return t < e.t;
 27     }
 28     inline bool operator ==(const Edge &e) const {
 29         return u==e.u || v==e.v;
 30     }
 31 };
 32 
 33 using namespace std;
 34 
 35 static int *parent;
 36 static int *rank_1;
 37 
 38 static int father(int p) {
 39 #if 0  // not optimized 
 40     while (parent[p] != p) {
 41         p = parent[p];
 42     }
 43     return p;
 44 #else
 45     return (parent[p] == p) ? p : (parent[p] = father(parent[p]));
 46 #endif 
 47 }
 48 
 49 int main(void) {
 50     ios::sync_with_stdio(false);
 51     
 52 /*#if 0 
 53     ifstream fin("input.txt");
 54     cin.rdbuf(fin.rdbuf());
 55 #endif*/ 
 56     
 57     int n, m, root;
 58     cin >> n >> m >> root;
 59     
 60     Edge *e = new Edge[m];
 61     parent = new int[n+1];
 62     rank_1 = new int[n+1];
 63     
 64     for(int i=0; i<m; ++i) {
 65         cin >> e[i].v >> e[i].u >> e[i].t;
 66     }
 67     for(int i=1; i<=n; ++i) {
 68         parent[i] = i;
 69         rank_1[i] = 1;
 70     }
 71     
 72     sort(e, e+m);
 73     
 74     int k = 0;
 75     int ans = 0;
 76     
 77     father(e[0].u);
 78     
 79     for(int i=0; i<m; ++i) {
 80         int pv = father(e[i].v);
 81         int pu = father(e[i].u);
 82         if (pv != pu) {
 83             if (rank_1[pv] < rank_1[pu]) {
 84                 parent[pv] = pu; // union pv -> pu 
 85             }
 86             else {
 87                 parent[pu] = pv; // union pu -> pv
 88                 if (rank_1[pu] == rank_1[pv]) {
 89                     ++rank_1[pv];
 90                 }
 91             }
 92             
 93             ++k;
 94             
 95             if (e[i].t > ans) {
 96                 ans = e[i].t;
 97             }
 98         }
 99         if (k==n-1) break;
100     }
101     
102     cout << ans << endl;
103     
104     return 0;
105 } 

 

PS:code

1. 解題時遇到一個罕見的問題:blog

  若在程序中定義標識符rank,則CSP報編譯錯誤。改成rank_1等標識符後經過。遞歸

這多是因爲判題系統佔用了rank這個標識符,致使編譯時出現重定義錯誤。ci

 

2. 傳輸時間爲什麼是路徑上的最大時間?

  能夠參考流水線實現,即各個節點的收發是異步的,對於一個節點,並非待全部數據都接收完畢後,再將收到的數據發送出去。這相似於雙端口緩存,寫端口(接收端送來數據)和讀端口(傳輸至發送端)同時以不一樣的速率寫入和讀出數據。

相關文章
相關標籤/搜索