ACM网络赛金华赛区的一道关于树的题:Family Name List

 三场网络赛终于告一段落了!唉,实力太弱了!跟北大、清华这些家伙差距太远了,比"我在你身边你却不知道我爱你"的距离还要远!

     这道题没有来得及提交,自己下来写完的,把样例过了!留在博客上作个记念吧!记念我大学生涯失败的网络赛!

    题目是这样的(英文原版):

 

 
 
  1. Description  
  2.  
  3. Kong belongs to a huge family. Recently he got a family name list which lists all men (no women) in his family over many generations.  The  list  shows  that  the  whole  family  has  a  common  ancestor,  let's  call  him  Mr.  X.  Of course,  everybody  except  Mr.X  in  the  list  is  Mr.  X's descendant.  Everybody's  father  is shown  in  the  list  except  that  Mr.  X's  father  is  not  recorded.  We  define  that  Mr.X's generation  number  is  0.  His  son's  generation  number  is  1.His  grandson's  generation number is 2, and so on. In a word, everybody's generation number is 1 smaller than his son's generation number. Everybody's generation number is marked in some way in the list.  
  4.  
  5. Now Kong is willing to pay a lot of money for a program which can re-arrange the list as he  requires ,and  answer  his  questions  such  as  how  many  brothers  does  a  certain  man have, etc. Please write this program for him.  
  6. Input  
  7. There are no more than 15 test cases.   
  8. For each test case:  
  9. The  first  line  is  an  integer  N(  1  <=  N  <=  30,000),  indicating  the  number  of  names  in  the list.  
  10. The second line is the name of Mr. X.  
  11. In  the  next  N-1  lines,  there  is  a  man's  name  in  each  line.  And  if  the  man's  generation number is K, there are K dots( '.') before his name.  
  12.   
  13. Please note that :  
  14. 1)  A name consists of only letters or digits( '0'-'9').  
  15. 2)  All names are unique.  
  16. 3)  Every line's length is no more than 60 characters.  
  17. 4)  In the list, a man M's father is the closest one above M whose generation number is 1 less  
  18. than M.  
  19. 5)  For any 2 adjacent lines in the list, if the above line's generation number is G1 and the lower  
  20. line' s generation number is G2, than G2 <= G1 +1 is guaranteed.    
  21.   
  22. After the name list, a line containing an integer Q(1<=Q<=30,000) follows, meaning that there are  
  23. Q queries or operations below.  
  24. In the Next Q lines, each line indicates a query or operation. It can be in the following 3 formats:  
  25. 1)  L  
  26. Print the family list in the same format as the input, but in a sorted way. The sorted way  
  27. means that: if A and B are brothers(cousins don’t count), and A's name is alphabetically  
  28. smaller than B's name, then A must appear earlier than B.   
  29. 2)  b name  
  30. Print out how many brothers does "name" have, including "name" himself.  
  31. 3)  c name1 name2  
  32. Print out the closest common ancestor of "name1" and "name2". "Closest" means the  
  33. generation number is the largest. Since Mr. X has no ancestor in the list, so it's guaranteed  
  34. that there is no question asking about Mr. X's ancestor.   
  35.   
  36. The input ends with N = 0.  
  37. Output  
  38. Already mentioned in the input.  
  39. Sample input  
  40. 9  
  41. Kongs  
  42. .son1  
  43. ..son1son2  
  44. ..son1son1  
  45. ...sonkson2son1  
  46. ...son1son2son2  
  47. ..son1son3  
  48. ...son1son3son1  
  49. .son0   
  50. 7  
  51. L  
  52. b son1son3son1  
  53. b son1son2  
  54. b sonkson2son1  
  55. b son1  
  56. c sonkson2son1 son1son2son2  
  57. c son1son3son1 son1son2  
  58. 0   

 这道题题意很简单,就是给你一个家谱关系树,然后问你几个问题:怎么将名字排序后输出?给你一个名字,查找他兄弟的个数;给你两个名字,查找他们最近公共祖先的名字。

 

刚开始,由于考虑到需要排序,我脑袋里冒出来的都是搜索树,set等可以自动排序的东西,东想西想,最后绚烂之极归于平淡,选择了结构体数组!这其实也是受线段树的启发,真的感觉最牛B的数据结构应该是数组!

好了,接下来困扰我好一会儿的问题就是:怎么实现结构体在set中的排序?

参考:http://cboard.cprogramming.com/cplusplus-programming/91781-stl-set-structs.html

一共有三种方式:

   1、结构体内部重载:

     bool operator<(const Node &a) const

{

      return a.age < age;

     }

    2、结构体外部重载:

bool operator<(const Node& left,const Node& right)

{

    return left.age < right.age;

}   

3、定义比较结构体:

struct cmp_age{

   inline bool operator()(const Node& left,const Node& right) const

    {

       return left.age < right.age;

    }

};

我个人觉得第3种方法最灵活。

这里稍微提一下的是STL中的priority_queue自定义的排序与第三种非常相似。

priority_queue<Node, vector<Node> ,cmp_age> q;

 

参见代码实现:

 

 
 
  1. /* 
  2.  * set_struct_sort.cpp 
  3.  * 
  4.  *  Created on: 2012-9-23 
  5.  *      Author: Administrator 
  6.  */ 
  7.  
  8. #include<iostream> 
  9. #include<set> 
  10. #include<string> 
  11. #include<queue> 
  12. #include<vector> 
  13. using namespace std; 
  14. struct Node{ 
  15.     int age; 
  16.     string name; 
  17.     //method 1 
  18. //   bool operator<(const Node &a) const 
  19. //   { 
  20. //    return a.age < age; 
  21. //   } 
  22. }; 
  23. //method 2 
  24. bool operator<(const Node& left,const Node& right) 
  25.     return left.age < right.age; 
  26. struct cmp_age{ 
  27.    inline bool operator()(const Node& left,const Node& right) const 
  28.     { 
  29.         return left.age < right.age; 
  30.     } 
  31. }; 
  32. struct cmp_name{ 
  33.    inline bool operator()(const Node& left,const Node& right) const 
  34.     { 
  35.         return left.name.compare(right.name)<=0; 
  36.     } 
  37. }; 
  38. set<Node,cmp_age> s1; 
  39. set<Node,cmp_name> s2; 
  40. set<Node> s3; 
  41. priority_queue<Node ,vector<Node> ,cmp_age > q; 
  42. void init_data_for_s_and_q(){ 
  43.     Node n1,n2,n3,n4; 
  44.     n1.age=1; n1.name="jack"
  45.     n2.age=6; n2.name="john"
  46.     n3.age=5; n3.name="Tom"
  47.     n4.age=12;n4.name="hank"
  48.     s1.insert(n1); 
  49.     s1.insert(n2); 
  50.     s1.insert(n3); 
  51.     s1.insert(n4); 
  52.     s2.insert(s1.begin(),s1.end()); 
  53.     s3.insert(s1.begin(),s1.end()); 
  54.  
  55.     q.push(n1);q.push(n2);q.push(n3);q.push(n4); 
  56. void print_data_for_s(){ 
  57.     set<Node,cmp_name>::iterator it; 
  58.     cout<<"\ns1 sort by age:"<<endl; 
  59.     it=s1.begin(); 
  60.     while(it!=s1.end()){ 
  61.         cout<<it->age<<", "<<it->name<<endl; 
  62.         it++; 
  63.     } 
  64.     cout<<"\ns2 sort by name:"<<endl; 
  65.     it=s2.begin(); 
  66.     while(it!=s2.end()){ 
  67.         cout<<it->age<<", "<<it->name<<endl; 
  68.         it++; 
  69.     } 
  70.     cout<<"\ns3 sort by age:"<<endl; 
  71.     it=s3.begin(); 
  72.     while(it!=s3.end()){ 
  73.         cout<<it->age<<", "<<it->name<<endl; 
  74.         it++; 
  75.     } 
  76.  
  77.     cout<<"\nprint priority_queue:"<<endl; 
  78.     Node n; 
  79.     while(!q.empty()){ 
  80.         n=q.top();q.pop(); 
  81.         cout<<n.age<<", "<<n.name<<endl; 
  82.     } 
  83. int main(){ 
  84.     init_data_for_s_and_q(); 
  85.     print_data_for_s(); 

好了,把思路拉回来,这道题最恼火的地方其实在于把数据存储在数组中,这里需要用到栈。这里我都感觉有点儿写不清楚了!因为结构体是这样定义的:

struct Node{

    string name;

    int parent,id;

    vector<int> sons;

};

一共四个元素:自己的名字,父亲在数组中的下标,自己输入时的id号(主要是排序后会用到),儿子结点在数组中的下标。

为了找到parent,这里需要借助刚才提到的栈father(用数组模拟)

最后还有一点的是:怎么快速找到名字在数组中对应的下标呢?用用map来存储就可以了。

好了,解释这么多了,贴代码吧!

 
 
  1. //============================================================================ 
  2. /* 
  3.  * 2012 金华区网络赛 Problem J Family Name List 
  4.  * */ 
  5. //============================================================================ 
  6.  
  7. #include <iostream> 
  8. #include<string> 
  9. #include<vector> 
  10. #include<set> 
  11. #include<map> 
  12. using namespace std; 
  13. #define M 30000 
  14. struct Node{ 
  15.     string name; 
  16.     int parent,id; 
  17.     vector<int> sons; 
  18. }; 
  19. bool operator< (const Node & lhs, const Node & rhs){ 
  20.     return lhs.name.compare( rhs.name)<=0; 
  21. map<string,int> ma; 
  22. Node r[M]; 
  23. int father[M],idx; 
  24. int level[M]; 
  25. //把家谱图排序后输出,这里用set解决 
  26. void L(int cur){ 
  27.     cout<<r[cur].name<<endl; 
  28.  
  29.     int len=r[cur].sons.size(); 
  30.     if(len!=0){ 
  31.         set<Node> s; 
  32.         set<Node>::iterator it; 
  33.         for(int i=0;i<len;i++){ 
  34.             s.insert(r[r[cur].sons.at(i)]); 
  35.         } 
  36.         it=s.begin(); 
  37.         while(it!=s.end()){ 
  38.             L(it->id); 
  39.             it++; 
  40.         } 
  41.     } 
  42. //计算兄弟的个数 
  43. int calBrother(const string &name){ 
  44.     map<string,int>::iterator it; 
  45.     it=ma.find(name); 
  46.     if(it!=ma.end()){ 
  47.         int id=ma.find(name)->second; 
  48.         return r[r[id].parent].sons.size(); 
  49.     } 
  50.     return -1; 
  51. //查找公共祖先 
  52. void commonAnestor(const string &name1,const string &name2){ 
  53.     if(name1.compare(name2)==0){ 
  54.         cout<<name1<<endl; 
  55.         return
  56.     } 
  57.     int id1=ma.find(name1)->second,id2=ma.find(name2)->second; 
  58.     if(id1==-1||id2==-1){ 
  59.         cout<<"error"<<endl; 
  60.         return
  61.     } 
  62.     string c1,c2; 
  63.     c1=r[r[id1].parent].name.substr(level[id1]-1); 
  64.     c2=r[r[id2].parent].name.substr(level[id2]-1); 
  65.     if(level[id1]==level[id2]){ 
  66.         commonAnestor(c1,c2); 
  67.     }else if(level[id1]<level[id2]){ 
  68.         commonAnestor(name1,c2); 
  69.     }else
  70.         commonAnestor(c1,name2); 
  71.     } 
  72. int main() { 
  73.     int n; 
  74.     while(1){ 
  75.         cin>>n; 
  76.         if(n==0)break
  77.         string name; 
  78.         idx=-1; 
  79.         for(int i=0;i<n;i++){ 
  80.             cin>>name; 
  81.             int j=0; 
  82.             while(name.at(j)=='.'){ 
  83.                 ++j; 
  84.             } 
  85.             level[i]=j; 
  86.             r[i].name=name; 
  87.             string cname=name.substr(j); 
  88.             ma.insert(pair<string,int>(cname,i)); 
  89.             r[i].id=i; 
  90.             if(idx!=-1){ 
  91.                 if(j==level[father[idx]]){ 
  92.                     father[idx]=i; 
  93.                 }else if(j>level[father[idx]]){ 
  94.                     father[++idx]=i; 
  95.                 }else{//j<level[idx] 
  96.                     idx-=(level[father[idx]]-j); 
  97.                     father[idx]=i; 
  98.                 } 
  99.                 r[i].parent=father[idx-1]; 
  100.                 r[father[idx-1]].sons.push_back(i); 
  101.             }else
  102.                 r[i].parent=-1; 
  103.                 father[++idx]=i; 
  104.             } 
  105.         } 
  106.         int m; 
  107.         cin>>m; 
  108.         string name1,name2; 
  109.         for(int i=0;i<m;i++){ 
  110.             cin>>name; 
  111.             if(name.compare("L")==0){ 
  112.                 L(0); 
  113.             }else if(name.compare("b")==0){ 
  114.                 cin>>name1; 
  115.                 cout<<calBrother(name1)<<endl; 
  116.             }else
  117.                 cin>>name1>>name2; 
  118.                 commonAnestor(name1,name2); 
  119.             } 
  120.         } 
  121.         ma.clear(); 
  122.         for(int i=0;i<n;i++){ 
  123.             r[i].sons.clear(); 
  124.         } 
  125.     } 
  126.     return 0; 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值