P2814 家谱
【题目描述】
输入由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系中父亲只有一行,儿子可能有若干行,用#name的形式描写一组父子关系中的父亲的名字,用+name的形式描写一组父子关系中的儿子的名字;接下来用?name的形式表示要求该人的最早的祖先;最后用单独的一个$表示文件结束。
【题目链接】
https://www.luogu.org/problemnew/show/P2814
【算法】
1.并查集:用stl,题目难度划分就不靠谱了,然而我傻傻的写了一个string转序号,序号转string的map调了半天,index变量名还不能用。。。然后几乎就是并查集模板。
2.树的父亲节点表示法:认真想一下发现这道题和并查集关联不大,没必要非要套并查集来做,其实本质上是树的父亲节点表示法,只要知道父亲,然后一路往上追溯就行了,当然可以用map把子节点和父亲连起来,比建树方便。当然并查集也同样是树形结构,其特点在于维护不相交的一系列集合:集合内部的元素相互间存在传递性,同时采用代表元法。
【代码1】
1 #include <bits/stdc++.h> 2 using namespace std; 3 int tot,p,first=1,tmp; 4 int fa[50010]; 5 map<string,int> h; 6 string rec[50010]; 7 string s; 8 int Get(int x) 9 { 10 if(fa[x]==x) return x; 11 return fa[x]=Get(fa[x]); 12 } 13 void Merge(int x,int y) 14 { 15 fa[x]=Get(y); 16 } 17 int main() 18 { 19 while(cin>>s&&s[0]!='$') { 20 if(h.find(s.substr(1))==h.end()) h.insert(make_pair(s.substr(1),++tot)),fa[tot]=tot,rec[tot]=s.substr(1); 21 p=h[s.substr(1)]; 22 if(s[0]=='#') tmp=h[s.substr(1)]; 23 else if(s[0]=='+') 24 Merge(p,tmp); 25 else { 26 if(first) { 27 for(int i=1;i<=tot;i++) Get(i); 28 first=0; 29 } 30 cout<<s.substr(1)<<" "<<rec[Get(p)]<<endl; 31 } 32 } 33 return 0; 34 }
【代码2】
1 #include <bits/stdc++.h> 2 using namespace std; 3 map<string,string> h; 4 string s,fa; 5 string Get(string s) 6 { 7 if(h.find(s)==h.end()) return s; 8 return Get(h[s]); 9 } 10 int main() 11 { 12 while(cin>>s&&s[0]!='$') { 13 if(s[0]=='#') fa=s.substr(1); 14 else if(s[0]=='+') h.insert(make_pair(s.substr(1),fa)); 15 else 16 cout<<s.substr(1)<<" "<<Get(s.substr(1))<<endl; 17 } 18 return 0; 19 }