程序设计天梯赛个人题解 L2-030-1 冰岛人

题目分析

综述

看输出和查询条件,知本题要考虑的有:

  1. 判断名字是否出现在输入中:储存输入并且查询是否在输入中出现即可
  2. 区分性别:通过姓名后缀即可判断性别
  3. 冰岛姓名寻找其父亲名字:去掉姓的后缀,即可得到父亲名字.因为只能得知父亲名字,还要考虑寻找父亲的姓.

储存结构的选择

综合考虑,采用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;
}
posted @ 2024-01-24 01:29  艾叶Minerva  阅读(110)  评论(0编辑  收藏  举报