世界上有許多宗教,你感興趣的是你學校裡的同學信仰多少種宗教。
你的學校有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 21 3
1 41 5
1 61 7
1 81 9
1 10
10 4
2 34 5
4 85 8
0 0case 1: 1
case 2: 7
「信仰同一宗教」這一關係是乙個等價關係(自反、傳遞、對稱),因此,很容易想到,最大的信仰數量是這一等價關係所匯出的等價類的數量。
看到這裡,立刻明白這道題是非常原始的並查集問題。
並查集是一種高效的記錄、查詢等價類的方式,它把屬於同乙個等價類的集合通過前驅記錄的形式構成一棵樹,並用根節點來代表整個等價類。
並查集支援find(查詢)、merge(合併)兩種基本的操作。
在並查集中,路徑壓縮是一項非常巧妙的設計,我們在find的同時,將迭代(或遞迴)查詢路徑上的節點都改為直接掛在根之下,這樣,不僅不影響的查詢的正確性,又縮減樹的深度,使得後續的find操作的代價趨近於乙個常數。
我會在**實現中,實現乙個基本的並查集模板。
#include
#pragma warning(disable: 4996)
class
mergefindset
~mergefindset()
intsize()
// 尋找i的祖宗,它代表i所屬的等價類
intfind
(int i)
// 合併i和j所屬的等價類
void
merge
(int i,
int j)
// 詢問i和j是否屬於同一等價類
bool
check
(int i,
int j)
// 計數有多少個等價類
intcount()
};intmain()
printf
("case %d: %d\n"
, icase, mfset-
>
count()
);delete mfset;
}return0;
}
並查集有很多變化,並不是所有都像這道題這麼簡單。
首先,有些題需要精巧地找到恰當地等價關係,一旦等價關係找錯,就等著wa吧。而有些並查集的題不僅需要維護乙個parents(前驅陣列),還需要維護一些額外的資訊,這些維護操作在find和merge過程中都可能發生。
宗教信仰(並查集)
問題描述 現如今世界上有如此多的宗教信仰,要將它們所有的都保持聯絡是非常困難的。你對於找出在乙個大學裡面同學們有多少不同的宗教信仰感興趣。你知道這個大學裡面有n個學生。如果讓你去乙個乙個問每乙個學生的宗教信仰是不可能的。而且,許多學生不願意表露他們的宗教信仰。有個方法可以避免這個問題,就是去問m 0...
並查集 宗教信仰
時間限制 1 sec 記憶體限制 128 mb 提交 15 解決 8 提交 狀態 討論版 世界上有許多不同的宗教,現在有乙個你感興趣的問題 找出多少不同的宗教,在你的大學中的大學生信仰了多少種不同的宗教。你知道在你的大學有n個學生 0輸入包含多組測試資料。每組測試資料的開頭包含兩個整數n和m。接下來...
宗教信仰(並查集)
問題描述 現如今世界上有如此多的宗教信仰,要將它們所有的都保持聯絡是非常困難的。你對於找出在乙個大學裡面同學們有多少不同的宗教信仰感興趣。你知道這個大學裡面有n個學生。如果讓你去乙個乙個問每乙個學生的宗教信仰是不可能的。而且,許多學生不願意表露他們的宗教信仰。有個方法可以避免這個問題,就是去問m 0...