NOI 4.3 圖論 1526:宗教信仰
阿新 • • 發佈:2019-01-27
1526:宗教信仰
總時間限制: 5000ms 記憶體限制: 65536kB
描述
世界上有許多宗教,你感興趣的是你學校裡的同學信仰多少種宗教。
你的學校有n名學生(0 < n <= 50000),你不太可能詢問每個人的宗教信仰,因為他們不太願意透露。但是當你同時找到2名學生,他們卻願意告訴你他們是否信仰同一宗教,你可以通過很多這樣的詢問估算學校裡的宗教數目的上限。你可以認為每名學生只會信仰最多一種宗教。
輸入
輸入包括多組資料。每組資料的第一行包括n和m,0 <= m <= n(n-1)/2,其後m行每行包括兩個數字i和j,表示學生i和學生j信仰同一宗教,學生被標號為1至n。輸入以一行n = m = 0
輸出
對於每組資料,先輸出它的編號(從1開始),接著輸出學生信仰的不同宗教的數目上限。
樣例輸入
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
樣例輸出
Case 1: 1
Case 2: 7
-----------------------------------------------------
思路
題意:已知圖中節點的連線關係,求圖中的連通支數量。
並查集模板題。關於並查集的詳細介紹,可以看這篇神奇的博文一個很有意思的並查集詳解
-----------------------------------------------------
程式碼
// 並查集 #include<iostream> #include<fstream> using namespace std; const int NMAX = 50005; int pre[NMAX] = {}; // 每個節點的父節點 int n = -1; // 全域性變數:學生人數 int cnt = n; // 全域性變數:總的集合個數 int find(int x) // 輸入一個節點的編號,返回該節點在並查集中的根節點 { int r = pre[x],t = x; while (r!=t) // 如果不是根節點(父節點==自己),則不斷上溯 { t = r; r = pre[t]; } // 路徑壓縮過程 t = x; int tt = x; while (t!=r) // 從葉子節點開始回溯,把路徑上的所有節點的父節點全都設為根節點 { tt = pre[t]; pre[t] = r; t = tt; } return r; } void join(int x, int y) // 合併兩個元素所在集合 { int xx = find(x), yy = find(y); // 分別找到兩個元素的根節點 if ( xx!= yy) // 如果兩個元素的根節點不同, 則需要合併; 否則兩個元素本來就在同一個集合 { cnt--; // 總的集合個數-1 pre[xx] = yy; // 將一個集合的根節點的父節點設為另一個集合的根節點 } } int main() { #ifndef ONLINE_JUDGE ifstream fin ("0403_1526.txt"); int m,i,x,y,t=0; while (fin >> n >> m) { if (n==0 && m==0) { break; } for (i=0; i<n; i++) { pre[i] = i; } cnt = n; for (i=0; i<m; i++) { fin >> x >> y; join(x,y); } cout << "Case "<< (++t) << ": " << cnt << endl; } fin.close(); #endif #ifdef ONLINE_JUDGE int m,i,x,y,t=0; while (cin >> n >> m) { if (n==0 && m==0) { break; } for (i=0; i<n; i++) { pre[i] = i; } cnt = n; for (i=0; i<m; i++) { cin >> x >> y; join(x,y); } cout << "Case "<< (++t) << ": " << cnt << endl; } #endif }