Week6--作業 -- B -- 戴好口罩[並查集問題]

題目描述

新型冠狀病毒肺炎(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 5
5 1 2 3 4 5
1 0
0 0

樣例輸出

4
1
1

思路

綜述

這道題主要考察了一個新的結構:並查集
參考:並查集

初始化:
void init() {
	for (int i = 0; i < maxn; i++) {
		par[i] = i;
		rnk[i] = 1;
	}
}
找父節點:

注意路徑壓縮語句 return par[x]=find(par[x]);

int find(int x) {
	if (par[x] == x)return x;
	else return par[x]=find(par[x]);
}
合併

這裏用到的方法是將小樹合併到大樹上面;
因爲用到了路徑壓縮,所以即使不將小樹合併到大樹上,採用隨機合併的方法,複雜度也是可以到常數級。

bool unit(int x,int y) {
	int px = find(x);
	int py = find(y);
	if (px == py)return false;
	if (rnk[px] < rnk[py]) swap(px, py);
	par[py] = px;
	rnk[px] = (rnk[py] += rnk[px]);
}

過程

Step1:小團體合併

記錄團體中的第一個人,然後後面的人都併到一個集合內

			cin >> first;
			for (int j = 1; j < num; j++) {
				cin >> member;
				unit(first, member);
			}
Step2:輸出

找到目標集合,輸出

		int virus = find(0);
		for (int i = 1; i < n; i++) {
			if (find(i) == virus)tot++;
		}
		cout << tot << endl;		

總結

unit中小樹合併到大樹是優化的一個方法;
但是採用路徑壓縮已經可以到常數級的複雜度。

代碼

上文有詳細註釋

#include <iostream>
using namespace std;
const int maxn = 1e5 + 5;
int par[maxn], rnk[maxn];

void init() {
	for (int i = 0; i < maxn; i++) {
		par[i] = i;
		rnk[i] = 1;
	}
}

int find(int x) {
	if (par[x] == x)return x;
	else return par[x]=find(par[x]);
}
int n, m;
bool unit(int x,int y) {
	int px = find(x);
	int py = find(y);
	if (px == py)return false;
	if (rnk[px] < rnk[py]) swap(px, py);
	par[py] = px;
	rnk[px] = (rnk[py] += rnk[px]);
}
int main() {
	while (cin >> n >> m) {
		if (n == 0 && m == 0)break;
		init();
		int num;
		int first;
		int member;
		for (int i = 0; i < m; i++) {
			cin >> num;
			if (num == 0)continue;
			cin >> first;
			for (int j = 1; j < num; j++) {
				cin >> member;
				unit(first, member);
			}
		}
		int tot = 1;
		int virus = find(0);
		for (int i = 1; i < n; i++) {
			if (find(i) == virus)tot++;
		}
		cout << tot << endl;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章