程序设计天梯赛个人题解 L2-030-1 冰岛人
题目分析
综述
看输出和查询条件,知本题要考虑的有:
- 判断名字是否出现在输入中:储存输入并且查询是否在输入中出现即可
- 区分性别:通过姓名后缀即可判断性别
- 冰岛姓名寻找其父亲名字:去掉姓的后缀,即可得到父亲名字.因为只能得知父亲名字,还要考虑寻找父亲的姓.
储存结构的选择
综合考虑,采用unordered_map<string,string>储存名和姓(带后缀)的映射,快速查询名对应的姓的同时,可以由unordered_map的性质(不存在的数据查询为空),判断 1.名字是否出现在输入中.
性别与后缀判断
由题设可知,若一个人的姓有后缀,则只可能是"sson","sdottir","m","f"四种,显然他们的末位字符不同,根据带后缀姓的末位字符我们便可判断其性别和后缀类型.
公共祖先判断(暴力)
显然对于一对冰岛人,要考虑其是否具有五代内公共祖先,对于外地人则不用考虑其祖先问题(当然从其姓名中也无法得知其祖先关系)
对于外地人,查询不到父,我们选择直接将其祖先返回为空;而冰岛人,则可以向上查询,返回父辈.
值得注意的是,题设提到:
所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。
也就是说,若a,b有共同的祖宗c,即使c是a的第5w辈祖宗,却是b的祖父,则ab不可以.c必须比任何一位的曾祖父辈分大才算"五代内无公共祖先"
所以,最简单易写的就是暴力遍历.
穷举a的所有祖先的同时,对于a的每个祖先都再穷举b的所有祖先.若任意一位相同,且在a或b的五代内,则说明二人有五代内公共祖先. 若两人当前查询到的祖先辈分都大于5,即使相同也不会有影响,只有任意一方祖先在五代内才会是有效的判断,而查询到祖先辈分大于5说明小于等于4的都已经判断过了.所以这种情况我们选择直接通过检查.(不跳过这种情况会导致最后一个数据超时)
可以发现这是个类LCA问题,,这里暴力属于是一种最低端的在线算法
个人示例代码
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
unordered_map<string, string> familyName; // familyName[givenName]=familyName
bool isMale(string name)
{
switch (familyName[name][familyName[name].size() - 1])
{
case 'm':
case 'n':
return 1;
break;
case 'f':
case 'r':
return 0;
break;
default:
return 0;
break;
}
}
string getFather(string name)
{
string father = familyName[name];
if(father.empty())
return "";
switch(father[father.size()-1])
{
case 'm':
case 'f':
return "";
break;
case 'n':
return string(father, 0, father.size() - 4);
break;
case 'r':
return string(father, 0, father.size() - 7);
break;
}
}
bool isFromSame(string a, string b)
{
int deepA, deepB;
string ta, tb;
//遍历a的任何一个祖宗
for (ta = a, deepA = 1; !ta.empty(); ta = getFather(ta), deepA++) // 经典链式遍历+深度记录
{
//对于a的每个祖宗,遍历b的任何一个祖宗进行比较
for (tb = b, deepB = 1; !tb.empty(); tb = getFather(tb), deepB++)
{
if (deepA >= 5 && deepB >= 5) // 双方都超过第五代则挑出查找
return false;
else if (ta == tb) // 如果至少一方在五代内,出现相同祖宗
return true;
}
}
return false;
}
// 坑点备注解析
// 如a,b有共同的祖宗c,其中c是a的第5w辈祖宗,却是b的祖父,则ab不可能.必须比任何一位的曾祖父辈分大
int main()
{
int n;
cin >> n;
while (n--)
{
string a, b;
cin >> a >> b;
familyName[a] = b;
}
int m;
cin >> m;
while (m--)
{
string a, b, tmp;
cin >> a >> tmp >> b >> tmp;
if (familyName[a].empty() || familyName[b].empty())
cout << "NA" << endl;
else if (!(isMale(a) ^ isMale(b)))
cout << "Whatever" << endl;
else if (isFromSame(a, b))
cout << "No" << endl;
else
cout << "Yes" << endl;
}
return 0;
}