L2-030 冰岛人 (25分)

题目链接:L2-030 冰岛人 (25分)

2018年世界杯,冰岛队因1:1平了强大的阿根廷队而一战成名。好事者发现冰岛人的名字后面似乎都有个“松”(son),于是有网友科普如下:
在这里插入图片描述

冰岛人沿用的是维京人古老的父系姓制,孩子的姓等于父亲的名加后缀,如果是儿子就加 sson,女儿则加 sdottir。因为冰岛人口较少,为避免近亲繁衍,本地人交往前先用个 App 查一下两人祖宗若干代有无联系。本题就请你实现这个 App 的功能。

输入格式:
输入首先在第一行给出一个正整数 N(1<N≤10^​5​​ ),为当地人口数。随后 N 行,每行给出一个人名,格式为:名 姓(带性别后缀),两个字符串均由不超过 20 个小写的英文字母组成。维京人后裔是可以通过姓的后缀判断其性别的,其他人则是在姓的后面加 m 表示男性、f 表示女性。题目保证给出的每个维京家族的起源人都是男性。
随后一行给出正整数 M,为查询数量。随后 M 行,每行给出一对人名,格式为:名1 姓1 名2 姓2。注意:这里的姓是不带后缀的。四个字符串均由不超过 20 个小写的英文字母组成。
题目保证不存在两个人是同名的。

输出格式:
对每一个查询,根据结果在一行内显示以下信息:
若两人为异性,且五代以内无公共祖先,则输出 Yes;
若两人为异性,但五代以内(不包括第五代)有公共祖先,则输出 No;
若两人为同性,则输出 Whatever;
若有一人不在名单内,则输出 NA。
所谓“五代以内无公共祖先”是指两人的公共祖先(如果存在的话)必须比任何一方的曾祖父辈分高。

输入样例:

15
chris smithm
adam smithm
bob adamsson
jack chrissson
bill chrissson
mike jacksson
steve billsson
tim mikesson
april mikesdottir
eric stevesson
tracy timsdottir
james ericsson
patrick jacksson
robin patricksson
will robinsson
6
tracy tim james eric
will robin tracy tim
april mike steve bill
bob adam eric steve
tracy tim tracy tim
x man april mikes

输出样例:

Yes
No
No
Whatever
Whatever
NA

解题思路:

  • 第一次写这题的时候,以为一个DFS就可以解决(跟之前那个天下姐妹那题好像),但是还是有所不同,看下图

  • 愿天下有情人都是失散多年的兄妹:
    在这里插入图片描述

  • 冰岛人:在这里插入图片描述

  • 所以,很明显姐妹那道DFS就行,但是DFS来处理这道题会很麻烦,那就用并查集的思路,解决它

题解:

  • 首先,字符串转换为整型来存储每一个节点,用map转换
  • 先进行输入处理来存储姓名,并判断是否为男、女。
  • 再对存储的数据,来进行转换为关系图,如下图
  • 最后,进行判断,先判断是否再名单,然后判断是否同sex,最后判断是否在五代以内有公共父节点

判断父节点,记录出现的次数和距离,距离小于4就不行

图片转载自:梅菜扣rou大佬

在这里插入图片描述

代码如下:

#include<iostream>
#include<vector>
#include<map>
#include<cstring>

using namespace std;

#define mm(a,x) memset(a,x,sizeof a)

const int N = 1e5 + 10;

int n,k;
//初始化为-1
vector<int > f(N,-1);
vector<string > g[N];
bool sex[N];
map<string,int > m;
string f1,f2;
int cnt;
int count[N],dist1[N],dist2[N];

bool judge(int a,int c){
	mm(count,0);mm(dist1,0);mm(dist2,0);
	//count记录某人出现的次数 
	count[a] ++;count[c] ++;
	int t;
	while(f[a] != -1){ 
		t = f[a];
		count[t] ++;
		//更新与根节点的距离 
		dist1[t] = dist1[a] + 1;
		if(t == c){ //直系
			return false;
		}
		a = t;
	}
	while(f[c] != -1){
		t = f[c];
		count[t] ++;
		dist2[t] = dist2[c] + 1;
		//出现次数大于1次,就表示有公共父节点了 
		if(count[t] > 1){
			//判断是否在五代以内,不在就可以,在就不可以 
			if(dist1[t] >= 4 && dist2[t] >= 4){
				return true;
			}else{
				return false;
			}
		}
		c = t; 
	}
	return true;
}
int main(){
	cin >> n;
	for(int i = 1; i <= n; i ++ ){
		cin >> f1 >> f2;
		m[f1] = ++ cnt;
		int len = f2.size();
		if(f2[len - 1] == 'm' || f2[len - 1] == 'n'){ //m和n都是男的 
			sex[cnt] = true;;
		}else{//r是女 
			sex[cnt] = false;
		}
		//先把它的姓名存起来,为下面构成家谱做铺垫 
		g[i].push_back(f1);
		g[i].push_back(f2);		
	}
	string par;
	for(int i = 1; i <= n; i ++ ){
		f1 = g[i][0];
		f2 = g[i][1];
		int len = f2.size();
		if(f2[len - 1] != 'r' && f2[len - 1] != 'n'){ //不是r也不是n,那就是祖宗呀 
			continue;
		}
		if(sex[m[f1]]){ 
			par = f2.substr(0,len - 4);
		}else{
			par = f2.substr(0,len - 7);
		}
		f[m[f1]] = m[par]; 
	}
	cin >> k;
	while(k -- ){
		string a,b,c,d;
		cin >> a >> b >> c >> d;
		//首先判断在不在名单里面 
		if(m.find(a) == m.end() || m.find(c) == m.end()){
			cout<<"NA\n";
			continue;
		}
		//其次判断是不是同性别 
		if(sex[m[a]] == sex[m[c]]){
			cout<<"Whatever\n";
			continue;
		}
		//最后,看公共父节点是否在五代以内 
		if(judge(m[a],m[c])){
			cout<<"Yes\n";
		}else{
			cout<<"No\n";
		}
	}
	return 0;
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值