您的位置:首页 > 理论基础 > 计算机网络

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

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

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

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

Description

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.

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.
Input
There are no more than 15 test cases.
For each test case:
The  first  line  is  an  integer  N(  1  <=  N  <=  30,000),  indicating  the  number  of  names  in  the list.
The second line is the name of Mr. X.
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.

Please note that :
1)  A name consists of only letters or digits( '0'-'9').
2)  All names are unique.
3)  Every line's length is no more than 60 characters.
4)  In the list, a man M's father is the closest one above M whose generation number is 1 less
than M.
5)  For any 2 adjacent lines in the list, if the above line's generation number is G1 and the lower
line' s generation number is G2, than G2 <= G1 +1 is guaranteed.

After the name list, a line containing an integer Q(1<=Q<=30,000) follows, meaning that there are
Q queries or operations below.
In the Next Q lines, each line indicates a query or operation. It can be in the following 3 formats:
1)  L
Print the family list in the same format as the input, but in a sorted way. The sorted way
means that: if A and B are brothers(cousins don’t count), and A's name is alphabetically
smaller than B's name, then A must appear earlier than B.
2)  b name
Print out how many brothers does "name" have, including "name" himself.
3)  c name1 name2
Print out the closest common ancestor of "name1" and "name2". "Closest" means the
generation number is the largest. Since Mr. X has no ancestor in the list, so it's guaranteed
that there is no question asking about Mr. X's ancestor.

The input ends with N = 0.
Output
Already mentioned in the input.
Sample input
9
Kongs
.son1
..son1son2
..son1son1
...sonkson2son1
...son1son2son2
..son1son3
...son1son3son1
.son0
7
L
b son1son3son1
b son1son2
b sonkson2son1
b son1
c sonkson2son1 son1son2son2
c son1son3son1 son1son2
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;

参见代码实现:

/*
* set_struct_sort.cpp
*
*  Created on: 2012-9-23
*      Author: Administrator
*/

#include<iostream>
#include<set>
#include<string>
#include<queue>
#include<vector>
using namespace std;
struct Node{
int age;
string name;
//method 1
//   bool operator<(const Node &a) const
//   {
//    return a.age < age;
//   }
};
//method 2
bool operator<(const Node& left,const Node& right)
{
return left.age < right.age;
}
struct cmp_age{
inline bool operator()(const Node& left,const Node& right) const
{
return left.age < right.age;
}
};
struct cmp_name{
inline bool operator()(const Node& left,const Node& right) const
{
return left.name.compare(right.name)<=0;
}
};
set<Node,cmp_age> s1;
set<Node,cmp_name> s2;
set<Node> s3;
priority_queue<Node ,vector<Node> ,cmp_age > q;
void init_data_for_s_and_q(){
Node n1,n2,n3,n4;
n1.age=1; n1.name="jack";
n2.age=6; n2.name="john";
n3.age=5; n3.name="Tom";
n4.age=12;n4.name="hank";
s1.insert(n1);
s1.insert(n2);
s1.insert(n3);
s1.insert(n4);
s2.insert(s1.begin(),s1.end());
s3.insert(s1.begin(),s1.end());

q.push(n1);q.push(n2);q.push(n3);q.push(n4);
}
void print_data_for_s(){
set<Node,cmp_name>::iterator it;
cout<<"\ns1 sort by age:"<<endl;
it=s1.begin();
while(it!=s1.end()){
cout<<it->age<<", "<<it->name<<endl;
it++;
}
cout<<"\ns2 sort by name:"<<endl;
it=s2.begin();
while(it!=s2.end()){
cout<<it->age<<", "<<it->name<<endl;
it++;
}
cout<<"\ns3 sort by age:"<<endl;
it=s3.begin();
while(it!=s3.end()){
cout<<it->age<<", "<<it->name<<endl;
it++;
}

cout<<"\nprint priority_queue:"<<endl;
Node n;
while(!q.empty()){
n=q.top();q.pop();
cout<<n.age<<", "<<n.name<<endl;
}
}
int main(){
init_data_for_s_and_q();
print_data_for_s();
}

好了,把思路拉回来,这道题最恼火的地方其实在于把数据存储在数组中,这里需要用到栈。这里我都感觉有点儿写不清楚了!因为结构体是这样定义的:
struct Node{
string name;
int parent,id;
vector<int> sons;
};
一共四个元素:自己的名字,父亲在数组中的下标,自己输入时的id号(主要是排序后会用到),儿子结点在数组中的下标。
为了找到parent,这里需要借助刚才提到的栈father(用数组模拟)。
最后还有一点的是:怎么快速找到名字在数组中对应的下标呢?用用map来存储就可以了。
好了,解释这么多了,贴代码吧!

//============================================================================
/*
* 2012 金华区网络赛 Problem J Family Name List
* */
//============================================================================

#include <iostream>
#include<string>
#include<vector>
#include<set>
#include<map>
using namespace std;
#define M 30000
struct Node{
string name;
int parent,id;
vector<int> sons;
};
bool operator< (const Node & lhs, const Node & rhs){
return lhs.name.compare( rhs.name)<=0;
}
map<string,int> ma;
Node r[M];
int father[M],idx;
int level[M];
//把家谱图排序后输出,这里用set解决
void L(int cur){
cout<<r[cur].name<<endl;

int len=r[cur].sons.size();
if(len!=0){
set<Node> s;
set<Node>::iterator it;
for(int i=0;i<len;i++){
s.insert(r[r[cur].sons.at(i)]);
}
it=s.begin();
while(it!=s.end()){
L(it->id);
it++;
}
}
}
//计算兄弟的个数
int calBrother(const string &name){
map<string,int>::iterator it;
it=ma.find(name);
if(it!=ma.end()){
int id=ma.find(name)->second;
return r[r[id].parent].sons.size();
}
return -1;
}
//查找公共祖先
void commonAnestor(const string &name1,const string &name2){
if(name1.compare(name2)==0){
cout<<name1<<endl;
return;
}
int id1=ma.find(name1)->second,id2=ma.find(name2)->second;
if(id1==-1||id2==-1){
cout<<"error"<<endl;
return;
}
string c1,c2;
c1=r[r[id1].parent].name.substr(level[id1]-1);
c2=r[r[id2].parent].name.substr(level[id2]-1);
if(level[id1]==level[id2]){
commonAnestor(c1,c2);
}else if(level[id1]<level[id2]){
commonAnestor(name1,c2);
}else{
commonAnestor(c1,name2);
}
}
int main() {
int n;
while(1){
cin>>n;
if(n==0)break;
string name;
idx=-1;
for(int i=0;i<n;i++){
cin>>name;
int j=0;
while(name.at(j)=='.'){
++j;
}
level[i]=j;
r[i].name=name;
string cname=name.substr(j);
ma.insert(pair<string,int>(cname,i));
r[i].id=i;
if(idx!=-1){
if(j==level[father[idx]]){
father[idx]=i;
}else if(j>level[father[idx]]){
father[++idx]=i;
}else{//j<level[idx]
idx-=(level[father[idx]]-j);
father[idx]=i;
}
r[i].parent=father[idx-1];
r[father[idx-1]].sons.push_back(i);
}else{
r[i].parent=-1;
father[++idx]=i;
}
}
int m;
cin>>m;
string name1,name2;
for(int i=0;i<m;i++){
cin>>name;
if(name.compare("L")==0){
L(0);
}else if(name.compare("b")==0){
cin>>name1;
cout<<calBrother(name1)<<endl;
}else{
cin>>name1>>name2;
commonAnestor(name1,name2);
}
}
ma.clear();
for(int i=0;i<n;i++){
r[i].sons.clear();
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐