2019 GDUT Rating Contest III : Problem E. Family Tree

题面:

E. Family Tree

Input file: standard input
Output file: standard output
Time limit: 1 second
Memory limit: 256 megabytes
 
Farmer John owns a family-run farm that has been passed down over several generations, with a herd of cows whose familial roots can similarly be traced back several generations on the same farm. By examining old records, Farmer John is curious how the cows in his current herd are related to each-other. Please help him in this endeavor!
 
 
Input
The first line of input contains N (1 ≤ N ≤ 100) followed by the names of two cows. Cow names are each strings of at most 10 uppercase letters (A...Z). Farmer John is curious about the relationship between the two cows on this line of input.
The next N lines each contain two cow names X and Y, indicating that X is the mother of Y.
 
Output
You should print one line of output indicating the relationship between the two cows specified on the first line of input (for simplicity, let’s call these two cows BESSIE and ELSIE for the examples below). Here are the different types of relationships that are possible:
  • You should output "SIBLINGS"if BESSIE and ELSIE have the same mother.
  • BESSIE might be a direct descendant of ELSIE, meaning that ELSIE is either the mother, grandmother, great-grand-mother, great-great-grand-mother, etc., of BESSIE. If this is the case, you should print "ELSIE is the (relation) of BESSIE where (relation) is the appropriate relationship, for example "great-great-grand-mother".
  • If ELSIE is a child of an ancestor of BESSIE (and ELSIE is not herself an ancestor or sister of BESSIE), then ELSIE is BESSIE’s aunt. You should output "ELSIE is the aunt of BESSIE"if ELSIE is a child of BESSIE’s grand-mother, "ELSIE is the great-aunt of BESSIE"if ELSIE is a child of BESSIE’s great-grand-mother, "ELSIE is the great-great-aunt of BESSIE"if ELSIE is a child of BESSIE’s great-great-grand-mother, and so on.
  • If BESSIE and ELSIE are related by any other means (i.e., if they share a common ancestor), they are cousins, and you should simply output "COUSINS".
  • You should output "NOT RELATED"if BESSIE and ELSIE have no common ancestor, or neither is directly descended from the other.
The following diagram helps illustrate the relationships above, which are the only relationship types you need to consider.
Observe that some relationships like "niece"(daughter of sister) are not necessary since if BESSIE is the niece of ELSIE, then ELSIE is BESSIE’s aunt.
 
Example
Input
7 AA BB
MOTHER AA
GGMOTHER BB
MOTHER SISTER
GMOTHER MOTHER
GMOTHER AUNT
AUNT COUSIN
GGMOTHER GMOTHER
 
Output
BB is the great-aunt of AA
 
 

题目描述:

农夫有关于奶牛的家庭关系树,问:两头牛之间的关系是什么。
 

题目分析:

这道题题目很容易理解,但关键是两头牛的关系究竟是怎样描述的?题目解释了很多,我们用图来解释有哪些情况(这里我用名字为AA,BB的牛来解释关系图):
关系图:

 

情况1:AA是BB的SISTER
 
我们分析这时候的情况有什么特点:首先,它们的最近公共祖先是MOTHER。其次,它们到MOTHER的距离都为1。所以,AA是BB的SISTER,或者BB是AA的SISTER.
 
情况2:AA是BB的MOTHER系列
 
特点:它们的最近公共祖先是AA(AA的最近公共祖先就是自己),AA到最近公共祖先的距离为0,BB到最近公共祖先的距离为1。这时,AA是BB的MOTHER。假如是这种情况:
最近公共祖先仍是AA,AA到最近公共祖先的距离为0,但是BB到最近公共祖先的距离为2。这时,AA还是BB的“MOTHER”,只不过是“grand-mother”。所以像这类情况的特点是:AA到最近公共祖先的距离为0,BB到最近公共祖先的距离不为0,这样它们就是“MOTHER”系列的关系。到底是哪种类型的“MOTHER”,要看BB到最近公共祖先的距离。
 
情况3:AA是BB的AUNT系列
 
特点:它们的最近公共祖先是GGMOTHER,AA到最近公共祖先的距离为1,BB到最近公共祖先的距离为2。所以,AA是BB的AUNT。假如是这种情况:
最近公共祖先仍是GGMOTHER,AA到最近公共祖先的距离仍为1,但是BB到最近公共祖先的距离为3。这时,AA仍是BB的“AUNT”,只不过是“great-aunt”。所以,像这类情况的特点是:AA到最近公共祖先的距离为1,BB到最近公共祖先的距离大于1,这样它们就是“AUNT”系列的关系。到底是什么“AUNT”,要看BB到最近公共祖先的距离。
 
情况4:AA是BB的COUSIN
 
特点:它们的最近公共祖先是GGMOTHER,AA到最近公共祖先的距离为2,BB到最近公共祖先的距离为2。那么这类情况有什么特点呢:其实就是除了情况1,2,3之外,能找到最近公共祖先就是情况4啦(题目说的😂),或者是AA和BB到最近公共祖先的距离距离大于等于2。
 
情况5:AA和BB之间没有关系
 
特点:找不到最近公共祖先。没有公共祖先的两头牛当然什么关系也没有啦。
 
说到这里,可能有些小伙伴还有一些疑惑:
1.最近公共祖先怎样找啊,怎样用代码去实现计算AA和BB到最近公共祖先的距离?
其实这道题就是很裸的lca题,其实步骤很简单:
  1. 初始化:建图,建记录每个结点的上一个结点的(父节点)数组,建深度数组
  2. 用dfs记录一遍深度
  3. 假如两个要找lca的结点的深度不同,先通过父节点数组使两个结点的深度相同,再一起通过父节点数组找lca
(看得懂上面的话可以自己尝试一下,不要看下面的解释那么快,看不懂的话就。。。ヾ(•ω•`)o  )首先,题目的输入是通过输入名字来建立关系树的,而用名字建图没有这么方便,所以我们可以为每一个名字进行编号,用这些编号来代表名字就比较方便。我是用c++ stl里面的map来实现这个功能的。那我们怎样建图呢?最方便的肯定是用二维数组建图啦。只需要开足够大的二维数组,就可以方便地访问访问两头牛之间“X”是否为“Y”的“MOTHER”。建完图后,我们还要弄一个“MOTHER”数组,也就是能找到它们的“MOTHER”的数组,这个在找最近公共祖先时会用到,我们可以先往下看。最后,还剩下一个“距离”数组,这个距离数组记录离“最开始”的那个点(这里的图最开始的点是GGMOTHER)到每个点的距离:
其中AA到最开始那个点的距离为2,BB到最开始那个点的距离为3,它们的最近公共祖先(GMOTHER)到最开始那个的点的距离为1。所以,要计算AA,BB到最近公共祖先的距离,只需要它们到最开始那个点的距离减去最近公共祖先到最开始那个点的距离就行了(这里AA:2-1=1,BB:3-1=2)。所以,问题就转化为求每个点到“最开始”那个点的距离:
看到这个箭头,是不是有点像二叉树的遍历?其实就是用类似于二叉树的深度遍历的方法,也就是dfs来记录这个距离。
 
记录完距离后,我们就要开始找最近公共祖先啦。但是在找之前,为了能同时让AA,BB找到它们的最近公共祖先,是不是要先把AA,BB到最近公共祖先的距离变得一样?这时"MOTHER"数组就派上用场了:只需要通过这样:AA/BB = MOTHER[AA/BB],再加一个循环,就可以使AA,BB到最近公共祖先的距离变得一样:
这时后再来一次(这次是同时找):AA = MOTHER[AA],BB = MOTHER[BB],当AA == BB时,就找到它们的最近公共祖先啦:
2.如果AA和BB的关系调转了呢,也就是这样:
这个好解决,直接一个swap函数反过来不久行了吗。(博主巨唠 ( ̄▽ ̄)" )
但是实际这个真的是毒瘤来的😭,swap只能用来交换下标和名字,不能用来交换距离,原因:交换距离后没有交换下标,导致后面读取还是那个下标,实际就是没什么用,还会卡循环(;´༎ຶД༎ຶ`),其实只要交换下标和名字就能达到目标效果了。
3.存图有什么注意事项:
按顺序存就行了,就像这样:
4.有多个“最开始”的点怎么办?
选一个能标记AA,BB的“最开始”的点就可以了,最开始的点就是入度为0的点(入度为0的点可以理解为这个点只有“出去”的箭头,而没有“进来”的箭头)。每个点的入度可以用一个数组in[]来实现,详情见代码。
5.这道题有坑吗?
当然有坑,输出要严格按照题目要求的输出答案格式去做,以上讲解全都是为了方便才这样弄的(比如AA是BB的SISTER,那么正确的输出应该是“SIBLINGS”而不是“AA is the sister of BB”;如果AA是BB的GGMOTHER,那么正确的输出应该是“AA is the great-grand-mother of BB”而不是“AA is the great-great-mother of BB”或者“AA is the grand-grand-mother of BB”,其他关系同理)
 
 
AC代码:
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 #include <cmath>
  5 #include <set>
  6 #include <map>
  7 #include <algorithm>
  8 using namespace std;
  9 int n;
 10 int cnt;   //牛的个数
 11 int G[205][205];  //存图
 12 int lca;   //最近公共祖先
 13 string u, v;
 14 
 15 map<string, int> name;   //为每头牛分配编号用
 16 int mother[205];         //每头牛的MOTHER
 17 int len[205];            //每头牛到最开始那头牛的距离
 18 int in[205];             //每头牛的入度
 19 
 20 void dfs_deep(int u, int deep){
 21     if(len[1] && len[2]) return;    //如果都获得了距离就结束
 22     len[u] = deep;                  //填入距离
 23     for(int i = 1; i <= cnt; i++){
 24         if(G[u][i]) dfs_deep(i, deep+1);   //往下继续遍历
 25     }
 26 }
 27 
 28 void find_lca(){
 29     int AA = 1, BB = 2;
 30     if(len[AA] < len[BB]) swap(AA, BB);   //交换下标
 31     while(len[AA] != len[BB]) AA = mother[AA];  //把距离远的拉到同一距离
 32     while(AA != BB){   //一起找最近公共祖先
 33         AA = mother[AA]; 
 34         BB = mother[BB];
 35     }
 36     lca = AA;   //存起来
 37 }
 38 
 39 void print(){
 40     int a = 1, b = 2;
 41     if(len[a] > len[b]) {
 42         swap(a, b);    //交换下标
 43         swap(u, v);    //交换名字
 44     }
 45 
 46     int disa = len[a]-len[lca], disb = len[b]-len[lca];
 47     
 48     if(disa == 1 && disb == 1)  //情况1
 49         cout << "SIBLINGS\n";
 50     else if(disa > 1)           //情况4
 51         cout << "COUSINS\n";
 52     else{
 53         cout << u << " is the ";
 54         if(disa == 0){          //情况2
 55             for(int i = 0; i < disb-2; i++)
 56                 cout << "great-";
 57             if(disb > 1) cout << "grand-";
 58             cout << "mother";
 59         }
 60         else{                   //情况3
 61             for(int i = 0; i < disb-2; i++)
 62                 cout << "great-";
 63             cout << "aunt";
 64         }
 65         cout << " of " << v << endl;
 66     }
 67 }
 68 
 69 int main(){
 70     cin >> n;
 71     cin >> u >> v;
 72     name[u] = ++cnt;   //分配编号
 73     name[v] = ++cnt;
 74 
 75     string x, y;
 76     for(int i = 0; i < n; i++){
 77         cin >> x >> y;
 78         if(!name[x]) name[x] = ++cnt;  //分配编号
 79         if(!name[y]) name[y] = ++cnt;
 80 
 81         int s = name[x], to = name[y];
 82         G[s][to] = 1;         //存边   
 83         in[to] = 1;           //标记入度不为0的点
 84         mother[to] = s;       //存MOTHER
 85     }
 86 
 87     for(int i = 1; i <= cnt; i++){
 88         if(!in[i]) {
 89             dfs_deep(i, 1);   //计算距离
 90             if(len[1] && len[2]) break;  //有一个尚未被标记就继续
 91             len[1] = len[2] = 0;
 92         }
 93     }
 94 
 95     if(!len[1] && !len[2]){    //情况5:找不到最近公共祖先等价于两个点没有被标记
 96         cout << "NOT RELATED\n";
 97         return 0;
 98     }
 99 
100     find_lca();   //找最近公共祖先
101     print();      //输出
102     return 0;
103 }

 

 
 

 

 
 
posted @ 2019-02-25 23:07  MrEdge  阅读(263)  评论(0编辑  收藏  举报