L2-030 冰岛人 (25分)
2018年世界杯,冰岛队因1:1平了强大的阿根廷队而一战成名。好事者发现冰岛人的名字后面似乎都有个“松”(son),于是有网友科普如下:
冰岛人沿用的是维京人古老的父系姓制,孩子的姓等于父亲的名加后缀,如果是儿子就加 sson,女儿则加 sdottir。因为冰岛人口较少,为避免近亲繁衍,本地人交往前先用个 App 查一下两人祖宗若干代有无联系。本题就请你实现这个 App 的功能。
输入格式:
输入首先在第一行给出一个正整数 N(1<N≤105),为当地人口数。随后 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
写这道题的过程还是蛮艰辛的,经历了几天的奋斗终于把所以的测试点都搞定了,还要多谢博客园的一位研究生大佬的帮助,没有他我还不知道要花多长的时间才能搞定
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include <time.h>
typedef struct Node{
char first_name[21] , last_name[21];
int sex , pos;
struct Node *pre;
}node;
node str[100005] , p , q;
typedef struct Pnode{
char str[21];
int pos;
struct Pnode *next;
}pnode;
pnode *array[26];
int n , m , i , pos[100005] , k , pos_p , pos_q;
void Function()//存入数据
{
char ch = str[i].first_name[0];
pnode *q = (pnode*)malloc(sizeof(pnode));
strcpy(q -> str , str[i].first_name);
q -> pos = i;
q -> next = array[ch - 'a'];
array[ch - 'a'] = q;
}
int function(char *q)//查找信息
{
char ch = q[0];
pnode *p = array[ch - 'a'];
while(p)
{
if(strcmp(p -> str , q) == 0) return p -> pos;//找到则返回该人的位置,否则返回0
p = p -> next;
}
return 0;
}
void Assignment()
{
scanf("%d",&n);
for(i = 1 ; i <= n ; i ++)
{
scanf("%s%s",str[i].first_name , str[i].last_name);
str[i].pos = i;
str[i].pre = NULL;
int len = strlen(str[i].last_name) , flag = 0;
switch(str[i].last_name[len - 1])
{
case 'm':str[i].sex = 1;str[i].last_name[len - 1] = '\0';break;
case 'f':str[i].sex = 0;str[i].last_name[len - 1] = '\0';break;
case 'n':str[i].sex = 1;str[i].last_name[len - 4] = '\0';flag ++;break;
case 'r':str[i].sex = 0;str[i].last_name[len - 7] = '\0';flag ++;break;
}
Function();//存放当前的人物信息
if(flag)
{
int cnnt = function(str[i].last_name);//查找当前人物的父亲或母亲
if(cnnt) str[i].pre = &str[cnnt];//i记录其祖先的位置
else pos[k ++] = i;//如果i之前没有输入i的父母或i就是第一代祖先就将i记录下来
}
}
for(i = 0 ; i < k ; i ++)//再次查找,解决后输入父母的问题
{
int temp = pos[i];
int cnnt = function(str[temp].last_name);
if(cnnt) str[temp].pre = &str[cnnt];
}
memset(pos , 0 , sizeof(pos));
}
int Final_treatment()
{
k = 0;
int len_p[100005] = {0} , len_q[100005] = {0} , flag_p = pos_p , flag_q = pos_q , Final_flag = 0;
pos[flag_p] = pos_p;//先从p查找一直到起源人,以pos_p标记每一代查找到的祖先,len_p记录每一代祖先到P的高度
while(str[flag_p].pre)
{
flag_p = str[flag_p].pre -> pos;
len_p[flag_p] = ++ k;
pos[flag_p] = pos_p;
}
k = 0;
if(pos[flag_q] == pos_p) return 0;//如果q是p的祖先直接返回0
while(Final_flag == 0 && str[flag_q].pre)
{
flag_q = str[flag_q].pre -> pos;//查找q的每一代祖先
len_q[flag_q] = ++ k;//标记每一代祖先到q的高度
if(pos[flag_q] == pos_p) Final_flag ++;//如果找到公共祖先就标记跳出
}
if(!Final_flag) return 1;//没有公共祖先就返回1
if(len_p[flag_q] > 3 && len_q[flag_q] > 3) return 1;//有公共祖先且到q,p的高度均大于3也返回1
return 0;//五代以内有公共祖先返回0
}
void Scend_treatment()
{
scanf("%d",&m);
for(i = 0 ; i < m ; i ++)
{
scanf("%s%s%s%s",p.first_name , p.last_name , q.first_name , q.last_name);
//查找p,q位置
pos_p = function(p.first_name);
pos_q = function(q.first_name);
if(pos_p && pos_q)
{
if(str[pos_p].sex == str[pos_q].sex) printf("Whatever\n");
else
{
int cnnt = Final_treatment();
if(cnnt) printf("Yes\n");
else printf("No\n");
}
}
else printf("NA\n");
}
}
int main()
{
Assignment();
Scend_treatment();
return 0;
}
这道题也算是一个分水岭吧!我将开始挑战L3的题目了,开始学图论了
上一篇: 大势至企业文件防泄密软件、数据泄密防护系统使用说明
下一篇: 网站建站前需要明确哪些工作