week6 戴好口罩! 並查集的基本應用

2021-10-04 15:50:52 字數 3561 閱讀 4882

分析總結

**【大家出門還是要戴好口罩噢?】

新型冠狀病毒肺炎(corona virus disease 2019,covid-19),簡稱「新冠肺炎」,是指2019新型冠狀病毒感染導致的肺炎。

如果乙個感染者走入乙個群體,那麼這個群體需要被隔離!

小a同學被確診為新冠感染,並且沒有戴口罩!!!!!!

危!!!

時間緊迫!!!!

需要盡快找到所有和小a同學直接或者間接接觸過的同學,將他們隔離,防止更大範圍的擴散。

眾所周知,學生的交際可能是分小團體的,一位學生可能同時參與多個小團體內。

請你編寫程式解決!戴口罩!!

多組資料,對於每組測試資料:

第一行為兩個整數n和m(n = m = 0表示輸入結束,不需要處理),n是學生的數量,m是學生群體的數量。0 < n <= 3e4 , 0 <= m <= 5e2

學生編號為0~n-1

小a編號為0

隨後,m行,每行有乙個整數num即小團體人員數量。隨後有num個整數代表這個小團體的學生。

輸出要隔離的人數,每組資料的答案輸出佔一行

100 4

2 1 2

5 10 13 11 12 14

2 0 1

2 99 2

200 2

1 55 1 2 3 4 5

1 00 041

1這道題就是一道經典的利用並查集的基本應用解決的題目。

並查集實際上就是通過陣列實現的集合結構。將屬於同一集合的放在一起,每個集合中有乙個代表元素,可以稱之為祖先,來代表和區分每個不同集合。

通過查詢每個元素的祖先和將乙個祖先的祖先設定為另乙個集合的祖先來實現集合的查詢和不同集合的合併。

基本功能:

int

finds

(int n)

//查詢祖先

void

unite

(int a,

int b)

//合併集合

並查集在使用前需要初始化,即每個元素其自身為乙個集合:

int

initialnize()

並查集中,將乙個元素指向它的上乙個父親,直到乙個元素的父親為其本身,說明這個元素為該集合的祖先。並查集利用陣列來模擬這個集合表示。每個下標對應的陣列空間中儲存的元素是該下標對應編號的節點或元素的父親的編號。當該下標對應編號的節點的父親的編號為其自身時,說明該節點為祖先。

sets[kid] = parent;

set[ancestor] = ancestor;

並查集並不複雜,並且在許多演算法設計中經常用於優化搜尋演算法的效能。但是並查集本身仍然可以進一步優化。

?優化

路徑壓縮(儲存結構優化)

容易想到,並查集可以化作乙個樹形結構,葉子表示孩子、父節點表示父親、根節點表示祖先,邊即代表兩個節點相關聯。一棵樹即為乙個集合,當乙個節點與一棵樹中的任意節點相連時,該節點就屬於這棵樹的根節點所代表的集合。

正因此,當從樹中任意乙個節點查詢該樹的祖先,也就是從任意乙個元素查詢其所在集合的祖先時,最大的時間複雜度為o(logn),即為樹的高度。

但實際上,在並查集中,父節點除了連線祖先與孩子之外,沒有其他實際含義。因此,若除祖先之外的節點都直接與祖先連線,那麼查詢的複雜度就可以優化為θ(1)。

int

finds

(int n)

//查詢祖先

合併(結構優化)

同上一點的分析,基於並查集的樹結構可知,兩個集合的合併,實際上就是兩棵樹的合併。也就是其中一棵樹的根節點成為另一棵樹根節點的葉子。

同樣基於搜素效能的最優化原則,當一棵並查集樹的層數越少時,其搜尋效能越好。所以我們要使兩棵樹合併後的新樹層數最少。

顯然,將元素少的樹掛在元素更多的樹之下,比元素多的樹掛在元素少的樹之下得到的新樹層數會更少。因此,將小樹掛在大樹上,能夠達到合併後結構最優化的結果。

int

initialnize()

}

void

unite

(int a,

int b)

//合併集合

題目給出的每個小團體,實際上就是乙個集合,代表了曾經互相接觸過的同學。

當分別來自兩個集合中的任意兩個同學接觸過後,就代表它們相互所在的集合與對方的集合間接接觸過。因此只要兩個元素出現在了同一團體,就應該將兩個元素所在的集合合併。

因此,只需要依次把每個小團體中的每個成員與其前乙個成員所在集合合併,就能將所有互相直接或間接接觸過的同學分到各自對應的大集合中。最後檢索0號同學所在集合的元素數量即可得到答案。

1.合併集合時的比較

合併集合時比較的不是兩個元素的父節點,而是兩個元素所在集合的祖先。

因為只有祖先才能唯一區分集合,而兩個元素的父節點不相同並不代表它們不來自同乙個集合。

而合併集合必須是針對兩個元素所在集合的祖先進行,否則就只是合併了這兩個元素,或是這兩個元素以及它們分別的父節點,這實際上只是最大集合的乙個子集合。

2. 為什麼答案不是儲存集合個數的陣列中下標對應0號同學的位置儲存的元素

儲存集合個數的的陣列中實際上是儲存的以其下標對應節點為祖先的集合之中元素個數。因為合併操作都是針對每個元素所在最大集合的祖先,所以集合中的其餘元素以其自身為祖先的集合實際上都沒有發生變化。

而在這個題目中,0號並不一定是答案集合的祖先,因此直接輸出以0號為祖先的集合元素個數肯定是錯誤的。而應該找到0號所在最大集合的祖先其對應的集合元素個數。

並查集雖簡單,但是總是寫了又忘,忘了又寫?

//

// main.cpp

// lab2

////

#include

#include

using

namespace std;

vector<

int>

student

(1000000,-

1);vector<

int>

numbers

(1000000,0

);intfinds

(int n)

//查詢祖先

void

unite

(int a,

int b)

//合併集合

intmain()

for(

int i =

0; i < m ; i++

)//把每個小團體的成員都放在第乙個成員的集合下

}

cout

)]

student.

clear()

; cin>>n>>m;

}return0;

}

WEEK6 周記 作業 並查集 戴好口罩

新型冠狀病毒肺炎 corona virus disease 2019,covid 19 簡稱 新冠肺炎 是指2019新型冠狀病毒感染導致的肺炎。如果乙個感染者走入乙個群體,那麼這個群體需要被隔離!小a同學被確診為新冠感染,並且沒有戴口罩!危!時間緊迫!需要盡快找到所有和小a同學直接或者間接接觸過的同...

WEEK6 作業 B 戴好口罩!

新型冠狀病毒肺炎 corona virus disease 2019,covid 19 簡稱 新冠肺炎 是指2019新型冠狀病毒感染導致的肺炎。如果乙個感染者走入乙個群體,那麼這個群體需要被隔離!小a同學被確診為新冠感染,並且沒有戴口罩!危!時間緊迫!需要盡快找到所有和小a同學直接或者間接接觸過的同...

Week6 作業B 戴好口罩!

新型冠狀病毒肺炎 corona virus disease 2019,covid 19 簡稱 新冠肺炎 是指2019新型冠狀病毒感染導致的肺炎。如果乙個感染者走入乙個群體,那麼這個群體需要被隔離!小a同學被確診為新冠感染,並且沒有戴口罩!危!時間緊迫!需要盡快找到所有和小a同學直接或者間接接觸過的同...