洛谷:P2814 家譜(並查集)

小何子啊發表於2020-10-07

題目背景
現代的人對於本家族血統越來越感興趣。

題目描述
給出充足的父子關係,請你編寫程式找到某個人的最早的祖先。

輸入格式
輸入由多行組成,首先是一系列有關父子關係的描述,其中每一組父子關係中父親只有一行,兒子可能有若干行,用 #name 的形式描寫一組父子關係中的父親的名字,用 +name 的形式描寫一組父子關係中的兒子的名字;接下來用 ?name 的形式表示要求該人的最早的祖先;最後用單獨的一個 $ 表示檔案結束。

輸出格式
按照輸入檔案的要求順序,求出每一個要找祖先的人的祖先,格式為:本人的名字 ++ 一個空格 ++ 祖先的名字 ++ 回車。

輸入輸出樣例

輸入 #1
#George
+Rodney
#Arthur
+Gareth
+Walter
#Gareth
+Edward
?Edward
?Walter
?Rodney
?Arthur
$

輸出 #1
Edward Arthur
Walter Arthur
Rodney George
Arthur Arthur

思路:
第一眼看是並查集,但是看這個輸入就暈了,比較難處理,要麼就是給每個人都標上記號,否則就只能用STL裡的關聯式容器map來做了(STL大法好!),把有關係的人記成:f[兒子]=父親,這樣就能記錄他倆的關係,至於用不用並查集,我想都打了關聯式容器了,我們就可以當成這是連結串列或者一棵樹?當我們輸入兒子要找祖先時,就一直向上找他的父親,當兒子的父親也有父親是們就更新最大父親(因為現在找的父親不是祖先),如果找的這個父親向上沒有父親了,證明他就是這個家族最大的父親(也就是祖先)!

程式碼:

#include<map>
#include<iostream>
#include<cstring>
using namespace std;
map<string,string>f;//關聯式容器map!
char s;//名字前的字元,判斷關係
string x,a;
int main(){
	while(s!='$'){
		cin>>s;
		if(s=='#') cin>>x,a=x;//如果是#號,證明是一個新父親,並用a來記錄新父親
		if(s=='+') cin>>x,f[x]=a;//如果是+號,證明是當前a父親的兒子,f容器記錄x和a的關係
		if(s=='?'){//是?號說明要尋找此人的祖先
			cin>>x;
			if(f[x].empty()){//如果此人本身就沒有父親,證明他就是自身的祖先
				cout<<x<<" "<<x<<endl;
				continue;
			}
			a=f[x];//a來記錄當前找到的最大祖先
			while(!f[a].empty()) a=f[a];//如果父親上面還有父親,就更新最大父親,用a記錄
			cout<<x<<" "<<a<<endl;//輸出
		}
	}
	return 0;
}