小韦老师@神犇营-my0414-家谱
题目:
描述
现代的人对于本家族血统越来越感兴趣,现在给出充足的父子关系,请你编写程序找到某个人的最早的祖先。
输入
输入由多行组成,首先是一系列有关父子关系的描述,其中每一组父子关系由二行组成,用 #name 的形式描写一组父子关系中的父亲的名字,用 +name 的形式描写一组父子关系中的儿子的名字;接下来用 ?name 的形式表示要求该人的最早的祖先;最后用单独的一个 $ 表示文件结束。规定每个人的名字都有且只有 6 个字符,而且首字母大写,且没有任意两个人的名字相同。最多可能有 1000 组父子关系,总人数最多可能达到 50000 人,家谱中的记载不超过 30 代。
输出
按照输入的要求顺序,求出每一个要找祖先的人的祖先,每一行的格式:本人的名字+一个空格+祖先的名字。
输入样例1
#George
+Rodney
#Arthur
+Gareth
+Walter
#Gareth
+Edward
?Edward
?Walter
?Rodney
?Arthur
$
输出样例1
Edward Arthur
Walter Arthur
Rodney George
Arthur Arthur
题解:
思路
整体思路:
这道题就是考查并查集,只是儿子和父亲从之前的整数都变成了字符串。
回顾之前我们用 int father[] 来存储并查集,做的是 儿子(整数)->父亲(整数) 的映射,而此处我们需要做 儿子(字符串)->父亲(字符串) 的映射,因此可以用 map 来完成。
其余的做法与一般的并查集一样。
具体步骤:
- 定义一个 map,用来做 儿子(字符串)->父亲(字符串) 的映射:
// 相当于一个下标为字符串类型,值也为字符串类型的数组
map<string, string> father;
- 定义 4 个字符串:
string str; // 暂存输入的字符串
string fa; // 存储父亲
string child; // 存储孩子
string query; // 查询
- 一直输入一个字符串,若输入的字符串为 $,则退出循环结束;否则做以下的操作:
if (str[0] == '#') { // 若 str 以 # 开头,则说明输入的是父亲
// 把 str 除了第一个位置以外都赋值给 fa
fa = str.substr(1, str.size() - 1);
if (father.count(fa) == 0) // 若 fa 未出现过
father[fa] = fa; // 初始化 fa
}
else if (str[0] == '+') { // 若 str 以 + 开头,则说明输入的是儿子
// 把 str 除了第一个位置以外都赋值给 child
child = str.substr(1, str.size()-1);
father[child] = fa; // 将该 child 的父亲置为刚刚输入的 fa
}
else if(str[0]=='?') { // 若 str 以 + 开头,则说明要进行查询
// 把 str 除了第一个位置以外都赋值给 query
query = str.substr(1, str.size()-1);
// 输出 query 以及 query 所在集合的根(祖先)
cout << query << " " << findFather(query) << endl;
}
4.实现查找函数:
// 查找 x 所在集合的根
string findFather(string x) {
while (x != father[x]) {
x = father[x];
}
return x;
}
思考:
1° 这道题的知识点可以分解为:并查集 + map,你能自己总结一下 map 的常用操作吗?顺便也可以总结下其他 STL 容器。
2° 这道题让你对并查集有了哪些新的认识和理解?
完整代码:
#include <bits/stdc++.h>
using namespace std;
// 用来存储 儿子-> 父亲 的映射
// 相当于一个下标为字符串类型,值也为字符串类型的数组
map<string, string> father;
// 查找 x 所在集合的根
string findFather(string x) {
while (x != father[x]) {
x = father[x];
}
return x;
}
int main() {
string str; // 暂存输入的字符串
string fa; // 存储父亲
string child; // 存储孩子
string query; // 查询
while (1) {
cin >> str; // 输入的字符串暂存在 str 中
if (str == "$") break; // 若 str 为 $,则退出循环结束
if (str[0] == '#') { // 若 str 以 # 开头,则说明输入的是父亲
// 把 str 除了第一个位置以外都赋值给 fa
fa = str.substr(1, str.size() - 1);
if (father.count(fa) == 0) // 若 fa 未出现过
father[fa] = fa; // 初始化 fa
}
else if (str[0] == '+') { // 若 str 以 + 开头,则说明输入的是儿子
// 把 str 除了第一个位置以外都赋值给 child
child = str.substr(1, str.size()-1);
father[child] = fa; // 将该 child 的父亲置为刚刚输入的 fa
}
else if(str[0]=='?') { // 若 str 以 + 开头,则说明要进行查询
// 把 str 除了第一个位置以外都赋值给 query
query = str.substr(1, str.size()-1);
// 输出 query 以及 query 所在集合的根(祖先)
cout << query << " " << findFather(query) << endl;
}
}
return 0;
}
我是小韦老师,企者不立,跨者不行,每天进步一点点。
欢迎大家多多交流,如果发现有错误,请多指正。有疑问的同学也可以留言评论或者发邮件。邮箱:weichangying_wcy@163.com