# 程序控

IPPP (Institute of Penniless Peasent-Programmer) Fellow

:: :: :: :: :: :: :: ::
 77 随笔 :: 0 文章 :: 442 评论 :: 0 引用

Time limit: 3.000 seconds

## Background背景

Expression trees, B and B* trees, red-black trees, quad trees, PQ trees; trees play a significant role in many domains of computer science. Sometimes the name of a problem may indicate that trees are used when they are not, as in the Artificial Intelligence planning problem traditionally called the Monkey and Bananas problem. Sometimes trees may be used in a problem whose name gives no indication that trees are involved, as in the Huffman code.

This problem involves determining how pairs of people who may be part of a "family tree" are related.

## The Problem问题

Given a sequence of child-parent pairs, where a pair consists of the child's name followed by the (single) parent's name, and a list of query pairs also expressed as two names, you are to write a program to determine whether the query pairs are related. If the names comprising a query pair are related the program should determine what the relationship is. Consider academic advisees and advisors as exemplars of such a single parent genealogy (we assume a single advisor, i.e., no co-advisors).

In this problem the child-parent pair p q denotes that p is the child of q. In determining relationships between names we use the following definitions:

• p is a 0-descendent of q (respectively 0-ancestor) if and only if the child-parent pair p q (respectively q p ) appears in the input sequence of child-parent pairs.
p是q的第0个后代，当且仅当输入序列中存在一个“子-父”对p q，指出了p和q为“子-父”关系。
• p is a k-descendent of q (respectively k-ancestor) if and only if the child-parent pair p r (respectively q r ) appears in the input sequence and r is a (k - 1) - descendent of q (respectively p is a (k-1)-ancestor of r).
p是q的第k个后代，当且仅当r是q的(k - 1)个后代，并且输入序列中存在一个“子-父”对p r，指出了p和r为“子-父”关系。

For the purposes of this problem the relationship between a person p and a person q is expressed as exactly one of the following four relations:

1. child -- grand child, great grand child, great great grand child, etc.
By definition p is the "child" of q if and only if the pair p q appears in the input sequence of child-parent pairs (i.e., p is a 0-descendent of q); p is the "grand child" of q if and only if p is a 1-descendent of q; and
子关系——包括孙“grand child”、曾孙“great grand child”、玄孙“great great grand child”等。
根据定义，p是q的“child”（子），当且仅当输入序列中存在一个“子-父”对p q，指出了它们为“子-父”关系（即p是q的第0个后代）；p是q的“grand child”（孙）当且仅当p是q的第1个后代；且：

if and only if p is an (n + 1)-descendent of q.
当且仅当p是q的第(n + 1)个后代。
2. parent -- grand parent, great grand parent, great great grand parent, etc.
父关系——包括祖父“grand parent”、曾祖父“great grand parent”、高祖父“great great grand parent”等。
By definition p is the "parent" of q if and only if the pair q p appears in the input sequence of child-parent pairs (i.e., p is a 0-ancestor of q); p is the "grand parent" of q if and only if p is a 1-ancestor of q; and
父关系的定义与子关系对应，参见上面的“子关系”。

if and only if p is an (n+1)-ancestor of q.
3. cousin -- 0th cousin, 1st cousin, 2nd cousin, etc.; cousins may be once removed, twice removed, three times removed, etc.
堂亲——第0堂亲“0th cousin”、第1堂亲“1st cousin”、第2堂亲等“2nd cousin”。堂亲存在隔代关系，隔1代、隔2代、隔3代等。
（译注：美国的堂亲关系与中国的相差很大，“nth cousin”中的n是指二人中辈份较长的一人与最近共同祖先相差的辈数减1。比如你和姑妈（父亲的亲姐妹）的最近 共同祖先是祖父，关系就是0th cousin；你和堂弟（姑妈的儿子）的最近共同祖先也是祖父，关系则是1st cousin；你和姨奶（奶奶的亲姐妹）儿子的关系是2nd cousin。两个cousin关系的人相差的辈数m，用“cousin removed m”来表示。）

By definition p and q are "cousins" if and only if they are related (i.e., there is a path from p to q in the implicit undirected parent-child tree). Let r represent the least common ancestor of p and q (i.e., no descendent of r is an ancestor of both p and q), where p is an m-descendent of r and q is an n-descendent of r.
根据定义，p和q为堂亲“cousins”，当且仅当他们存在亲属关系（即在家谱树中隐含着一条不论方向的由p至q的路径）。令r表示p和q的最近共同祖先（即r没有后代也是p和q的共同祖先），p是r的第m个后代，q是r的第n个后代。
Then, by definition, cousins p and q are "kth cousins" if and only if k = min(n, m), and, also by definition, p and q are "cousins removed j times" if and only if j = |n - m|.
那么根据定义，堂亲p和q是“kth cousin”当且仅当k = min(n, m)。p和q是“cousin removed j times”当且仅当j = |n - m|。
4. sibling -- 0th cousins removed 0 times are "siblings" (they have the same parent).
同胞——“0th cousins removed 0 times”为同胞“siblings”（他们有共同的父）。

## The Input输入

The input consists of parent-child pairs of names, one pair per line. Each name in a pair consists of lower-case alphabetic characters or periods (used to separate first and last names, for example). Child names are separated from parent names by one or more spaces. Parent-child pairs are terminated by a pair whose first component is the string "no.child". Such a pair is NOT to be considered as a parent-child pair, but only as a delimiter to separate the parent-child pairs from the query pairs. There will be no circular relationships, i.e., no name p can be both an ancestor and a descendent of the same name q.

The parent-child pairs are followed by a sequence of query pairs in the same format as the parent-child pairs, i.e., each name in a query pair is a sequence of lower-case alphabetic characters and periods, and names are separated by one or more spaces. Query pairs are terminated by end-of-file.
“子-父”对下面是待查对，格式与“子-父”对相同，即每个待查对中的名字都由小写字母和点号构成，两个名字间由1个或多个空格隔开。待查对的输入由EOF结束。

There will be a maximum of 300 different names overall (parent-child and query pairs). All names will be fewer than 31 characters in length. There will be no more than 100 query pairs.

## The Output输出

For each query-pair p q of names the output should indicate the relationship p is-the-relative-of q by the appropriate string of the form

• child, grand child, great grand child, great great ...great grand child
• parent, grand parent, great grand parent, great great ...great grand parent
• sibling
• n cousin removed m
• no relation

If an m-cousin is removed 0 times then only m cousin should be printed, i.e., removed 0 should NOT be printed. Do not print st, nd, rd, th after the numbers.

## Sample Input输入示例

alonzo.church oswald.veblen
stephen.kleene alonzo.church
dana.scott alonzo.church
martin.davis alonzo.church
pat.fischer hartley.rogers
mike.paterson david.park
dennis.ritchie pat.fischer
hartley.rogers alonzo.church
les.valiant mike.paterson
bob.constable stephen.kleene
david.park hartley.rogers
no.child no.parent
stephen.kleene bob.constable
hartley.rogers stephen.kleene
les.valiant alonzo.church
les.valiant dennis.ritchie
dennis.ritchie les.valiant
pat.fischer michael.rabin

## Sample Output输出示例

parent
sibling
great great grand child
1 cousin removed 1
1 cousin removed 1
no relation

## Solution解答

#include <algorithm>
#include <iostream>
#include <map>
#include <vector>
#include <string>
using namespace std;
struct NODE {int nPar; int nPos; vector<int> Chi;};
vector<NODE> Tree;
//分别用于存储RQM的遍例次序和深度
vector<int> Order, Depth;
//由树建立RQM表，递归方式
void BuildRMQTable(int nNode, int nDepth) {
//进入当前节点，存储其编号和深度
Order.push_back(nNode);
Depth.push_back(nDepth);
NODE &Node = Tree[nNode];
//第一次遍例该节点，记录节点在表中的位置
Node.nPos = Node.nPos == -1 ? (Order.size() - 1) : Node.nPos;
//多叉树的标准深度遍例方式，依次访问所有子节点
for (vector<int>::iterator i = Node.Chi.begin(); i != Node.Chi.end();) {
BuildRMQTable(*i++, nDepth + 1);
//回到当前节点，存储其编号和深度
Order.push_back(nNode);
Depth.push_back(nDepth);
}
}
//主函数
int main(void) {
//姓名Hash
map<string, int> NameTbl;
//新节点的初始值，父为-1，RMQ表位置为-1
NODE NewNode = {-1, -1};
//循环输入所有的姓名，str1为子，str2为父
for (string str1, str2; cin >> str1 >> str2 && str1 != "no.child";) {
//不存在给定名称，则加入该名称
if (NameTbl.end() == NameTbl.find(str2)) {
NameTbl[str2] = Tree.size();
Tree.push_back(NewNode);
}
if (NameTbl.end() == NameTbl.find(str1)) {
NameTbl[str1] = Tree.size();
Tree.push_back(NewNode);
}
//建立父子关系
Tree[NameTbl[str2]].Chi.push_back(NameTbl[str1]);
Tree[NameTbl[str1]].nPar = NameTbl[str2];
}
//为避免出现"多树"的情况，建立虚拟的总根，放在列表最后
Tree.push_back(NewNode);
//遍例所有节点
for (vector<NODE>::iterator i = Tree.begin(); i != Tree.end() - 1; ++i ) {
//找出没有父的节点，即父为-1的节点
if (i->nPar == -1) {
//令父其父节点为总根
i->nPar = Tree.size() - 1;
//在总根的子节点列表中加入该节点
Tree.back().Chi.push_back(i - Tree.begin());
}
}
//从总根开始递归建立RMQ表
BuildRMQTable(Tree.size() - 1, 0);
//循环输入每一组查询
for (string str1, str2; cin >> str1 >> str2;) {
map<string, int>::iterator i1 = NameTbl.find(str1);
map<string, int>::iterator i2 = NameTbl.find(str2);
//如果两个名子中有任一个没有记录，认为无关
if (i1 == NameTbl.end() || i2 == NameTbl.end()) {
cout << "no relation" << endl;
continue;
}
//得到两个名子在查询表中的位置
int n1 = Tree[i1->second].nPos, n2 = Tree[i2->second].nPos;
//保持位置较小者在前
if (Depth[n1] > Depth[n2]) {
swap(n1, n2);
}
//RQM查询
vector<int>::iterator iAnc = min_element(
Depth.begin() + min(n1, n2), Depth.begin() + max(n1, n2) + 1);
//如果小最共同祖先(LCS)为总根，认为无关
if (Tree[Order[iAnc - Depth.begin()]].nPar == -1) {
cout << "no relation" << endl;
continue;
}
//nRemoved为隔代数，nCousin为二者与LCS距离的最小值
int nRemoved = Depth[n2] - Depth[n1];
int nCousin = Depth[n1] - *iAnc;
//二者中有一人为LCS的情况
if(nCousin == 0) {
for (; nRemoved > 2; --nRemoved) {
cout << "great ";
}
if (nRemoved > 1) {
cout << "grand ";
}
cout << (Tree[i1->second].nPos == n1 ? "parent" : "child") << endl;
}
//LCS为二者生父
else if (nCousin == 1 && nRemoved == 0) {
cout << "sibling" << endl;
}
//堂亲
else {
cout << nCousin - 1 << " cousin";
if (nRemoved > 0) {
cout << " removed " << nRemoved;
}
cout << endl;
}
}
return 0;
}

posted on 2010-08-12 21:32  Devymex  阅读(...)  评论(...编辑  收藏