UVa OJ 115 – Climbing Trees (家譜樹)

來源:互聯網
上載者:User

Time limit: 3.000 seconds
限時:3.000秒

 

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.
運算式樹狀架構、B/B*樹、紅/黑樹狀結構、四叉樹、PQ樹……,樹在電腦科學的許多領域中都扮演了重要的角色。有些問題的名稱表明其使用了樹結構,比如智能規劃問題在傳統上稱作“猴子與香蕉”問題。有些問題確實用到了樹的結構,但在它的名稱中並沒有表示出來,比如哈夫曼編碼。

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 q表示p是q的子。在確定姓名關係的過程中,我們有以下的歸納定義:

  • 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:
就此問題而言,p和q兩人之間的親屬關係只能是以下4種關係中的一種:

  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.
輸入由一系列的名字對構成,每對獨佔一行。各對中的名字都由小寫字母和點號(比如可用來分隔姓和名)構成。子和父之間由1個或多個空格隔開。當輸入的姓名對前者的名字為“no.child”時,表示“子-父”對輸入結束。結束符僅僅用於分隔“子-父”對和待查對,程式不能將其作為一個“子-父”對來處理。輸入中不會存在迴圈關係,即任何名字p都不可能同為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.
總共最多出現300個不同的名字(包括“子-父”對和待查對)。所有姓名都少於31個字元長度。最多100個待查對。

 

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
對於每個姓名待查對,都要以下列的輸出形式表示p與q的關係

  • 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.
如果一個“m cousin”相隔0代,那麼只需列印出“m cousin”,也就是說輸出中不能出現“removed 0”。不要在任何數字後面添加“st”、“nd”、“rd”、“th”等尾碼。

 

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

 

Analysis
分析

這是一道典型的關於LCA(最近公用祖先)演算法的題目。跟據這道題的特點,將LCA問題轉換為RQM問題是很自然的做法。關於轉換的演算法和RQM問題的高效演算法我將在另外的文章中給出。

按照題目的要求,一個子只能存在一個父(we assume a single advisor, i.e., no co-advisors),因此這道題的解法其實可以非常多。然而非常操蛋的是評判所用資料中確實出現了一子多父的情況。如果你忽略了這樣的非法輸入,將會得到WA。程式必須能夠“正確”的處理這樣的異常,但是處理的方法文中並沒有給出。

事實上的處理方式是沒有規律的,讓未考慮多父情況的轉換RQM演算法強制運行在這種異常的輸入情況下,得到的結果就是所謂的“正確”結果。如果你一不小心選擇了與出題人思路不同的演算法,那麼無論你將程式寫的多麼萬無一失,無論你如何處理多父的情況,你也永遠只能得到WA。

我在這道題上卡了相當長的時間,原因就是我的高效演算法不能滿足出題人的變態要求。極度反感這道題!血淚教訓:必須使用將LCA問題轉換為RMQ問題的演算法,其它演算法一概WA!

 

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) {//姓名Hashmap<string, int> NameTbl;//新節點的初始值,父為-1,RMQ表位置為-1NODE 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;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.