原题链接:P2814 家谱 - 洛谷
解析参考:P3367【模板】并查集
什么是并查集:
我们假定 f [ i ] f[i] f[i]表示第i个人的老大是谁。
现在,我们有甲,乙,丙打架了,甲做了乙的小弟。则有
f
[
a
]
f[a]
f[a]=
b
b
b,后来甲打赢了丙,那么丙就是甲的小弟了。有
f
[
c
]
=
a
f[c]=a
f[c]=a,但是如果我们这样表示,丙不能直接知道乙是最大的大哥。所以我们必须直接让乙变成最大的老大,为了避免自己人打自己人,于是定义函数
f
i
n
d
(
)
find()
find()
int find(int k){
if(f[k]==k) return k;
return find(f[k]);
}//find函数可以直接找到最大的老大
f[c]=find(a); //找到丙的最大的老大
这时,因为我们要路过他所有的上级,我们也可以顺便使寻找的途中经过的人的大哥也变成"最大的老大",于是进行路径压缩
//路径压缩
int find(int k){
if(f[k]==k) return k;
return f[k]==find(f[k]);
/*
也即;
f[k]=find(f[k]);
return f[k];
*/
}//递归版 find 函数
//循环版 find 函数
int find(int x){
while(x!=f[x]) x=f[x]=f[f[x]];
//让x 和x的老大变成统一变成最大的老大
return x;
}
判断两个人的老大是否是同一个人,只需:
if(find(a)==find(b))
规定:
- 一个人不能有两个老大
- 当已经有老大的人臣服时,老大也将成为胜利的人的小弟
该题思路:
- 题目中的father 便是上面说的老大
- 利用STL中的map结构进行 < s t r i n g , s t r i n g > <string,string> <string,string>的映射
AC代码:
// 家谱 P2814
#include<bits/stdc++.h>
using namespace std;
map<string,string>g; //father son
string s,s1;
string find(string x)
{
if(x!=g[x])
g[x]=find(g[x]);
return g[x];
}
int main()
{
char ch;
cin>>ch;
while(ch!='$')
{
cin>>s;
if(ch=='#')
{
s1=s;
if(g[s]=="") g[s]=s;
}
else if(ch=='+')
g[s]=s1;
else
cout<<s<<' '<<find(s)<<endl;
cin>>ch;
}
return 0;
}