Description
VonNeumann is the CEO of this organization. VonNeumann has two direct subordinates: Tanenbaum and Dijkstra. Members of the organization who are direct subordinates of the same member are ranked by their respective seniority. In the diagram, the seniority of such members decrease from left to right. For example Tanenbaum has higher seniority than Dijkstra.
When a member hires a new direct subordinate, the newly hired subordinate has lower seniority than any other direct subordinates of the same member. For example, if VonNeumann (in Figure 1) hires Shannon, then VonNeumann's direct subordinates are Tanenbaum, Dijkstra, and Shannon in order of decreasing seniority.
When a member of the organization gets fired, there are two possible scenarios. If the victim (the person who gets fired) had no subordinates, then he/she will be simply dropped from the organization's hierarchy. If the victim had any subordinates, then his/her highest ranking (by seniority) direct subordinate will be promoted to fill the resulting vacancy. The promoted person will also inherit the victim's seniority. Now, if the promoted person also had some subordinates then his/her highest ranking direct subordinate will similarly be promoted, and the promotions will cascade down the hierarchy until a person having no subordinates has been promoted. In Figure 1, if Tanenbaum gets fired, then Stallings will be promoted to Tanenbaum's position and seniority, and Knuth will be promoted to Stallings' previous position and seniority.
Figure 2 shows the hierarchy resulting from Figure 1 after (1) VonNeumann hires Shannon and (2) Tanenbaum gets fired:
Input
The first line will be followed by one or more additional lines. The format of each of these lines will be determined by one of the following three rules of syntax:
- [existing member] hires [new member]
- fire [existing member]
Here [existing member] is the name of any individual who is already a member of the organization, [new member] is the name of an individual who is not a member of the organization as yet. The three types of lines (hires, fire, and print) can appear in any order, any number of times.
You may assume that at any time there is at least one member (who is the CEO) and no more than 1000 members in the organization.
Output
- Each line in the textual representation of the tree will contain exactly one name.
- The first line will contain the CEO's name, starting in column 1.
- The entire tree, or any sub-tree, having the form
will be represented in textual form as:
The output resulting from each print command in the input will be terminated by one line consisting of exactly 60 hyphens. There will not be any blank lines in the output.
Sample Input
VonNeumann VonNeumann hires Tanenbaum VonNeumann hires Dijkstra Tanenbaum hires Stallings Tanenbaum hires Silberschatz Stallings hires Knuth Stallings hires Hamming Stallings hires Huffman print VonNeumann hires Shannon fire Tanenbaum print fire Silberschatz fire VonNeumann print
Sample Output
VonNeumann +Tanenbaum ++Stallings +++Knuth +++Hamming +++Huffman ++Silberschatz +Dijkstra ------------------------------------------------------------ VonNeumann +Stallings ++Knuth +++Hamming +++Huffman ++Silberschatz +Dijkstra +Shannon ------------------------------------------------------------ Stallings +Knuth ++Hamming +++Huffman +Dijkstra +Shannon ------------------------------------------------------------
读懂题意后,这道题就不难解决了。这道题主要运用的知识点有:树,map容器,list容器。
1.我们可以用一个结构体来存储每个根成员(员工)的姓名和他的下属(即儿子队列);
比如CEO,建立一个CEO成员的结构体,并将其作为树的根,他的儿子队列用list容器来存储,当然这里存储的是结构体类的地址,包含了父节点的所有儿子;
儿子队列则按照员工资历来存储,资历最高的放在左侧首节点,资历最低的放在右侧尾节点。
2.然后,雇佣新员工,name1雇佣name2,name1是已知存在的员工,我们要雇佣name2则需要找到name1所在位置,然后将name2员工插入到name1的儿子队列的末尾,
插入队列我们则用到了list容器,用它来建立一个链表存储name1的儿子队列。
问题是该如何找到员工name1呢?显然最容易想到的便是遍历整个树,而直接遍历整个树的话是非常麻烦的,而且不容易查找。那我们可不可以用个简单的方法来实现查找呢?
想到之前接触到的map容器,不正好解决了这个问题吗?我们将员工的名字作为key(关键字),每个员工的所存地址作为索引,将所有员工的名字和数据保存地址一一对应,
需要某个员工信息则直接可以通过key就找到了员工的位置。这样就简单多了,我们就在建立每个员工结构体的时候,把每个员工的姓名和地址存入到map容器中,这样方便我们对员工信息的查找,实际上,这个map容器就是一个哈希表。
3.然后就是解雇了,解雇理解起来比较麻烦难懂,仔细琢磨一下就理解透了。要解雇员工name,就要将name在这个树中移除,并且将name在哈希表中移除,我们取name员工所在的位置指针为p,根据题意,name被解雇,他的位置p依然存在且不能是空的,需要name的儿子队列中资历最高的成员来继承他的职位,资历最高的成员也就是儿子队列中的首节点了,我们将这个首节点的名字复制到name员工所在位置p->name中,取代被解雇员工的名字,其儿子队列指针依然是原先的,首节点先不做删除,我们等到首节点的儿子队列为空(即没有下属员工)的时候在将他的首节点进行删除操作。如果首节点的儿子队列不为空,我们将p的指针移动到首节点,然后循环进入下一次提升操作。直到儿子队列中资历最高的成员没有下属成员为止。然后将该成员的父节点p的儿子队列进行删除首节点操作。这就达到了解雇的目的,更新整个树。
4.最后是打印,从根开始,依次递归打印每一层成员的姓名。
下面是实现代码:
#include <iostream>
#include <string>
#include <cstring>
#include <list>
#include <map>
using namespace std;
struct Tman
{
string name; //姓名
Tman *f; //父节点指针
list<Tman *> s; //存储儿子的队列指针s
Tman(){ f = NULL; } //初始化构造函数,将父节点指针初始化为空
};
map<string, Tman *> hash; //hash[x]存储名为x成员(即以成员名为x为根的子树)的指针
Tman *root; //根节点指针
void hires( string name1, string name2 )
{
Tman *f = hash[name1]; //取该name1(已存在的)成员的指针
Tman *p = new Tman(); //建立新节点*p,以存储新雇佣的下属
p->name = name2;
p->f = f;
f->s.push_back( p ); //将新节点p插入到父节点name1的儿子队列中
hash[name2] = p; //用哈希表记录新节点name2的地址
}
void fire( string name )
{
Tman *p = hash[name]; //取该成员的指针
Tman *f = p->f; //取该成员的父节点指针
hash.erase( name ); //释放掉以该成员为根的子树
while( p->s.size() != 0 ) //从该成员p出发,将其左链上的节点依次提升一个层次,直到成员没有儿子队列为止。
{
p->name = p->s.front()->name; //将该成员p的儿子队列s中的首节点成员的名字提升到该成员p的位置(姓名替换掉)
hash[p->name] = p; //然后将哈希表中原先存储的名为p->name成员(即要解雇成员p的下属资历最高的成员)
// 的指针更换为当前位置(提升后)的指针
p = p->s.front(); //然后p指针移动到p提升前的要解雇的成员name的儿子队列首节点指针,
//这是p指针移动到了下一层的资历最高的成员位置了,进入下一次循环,
//将左链的资历最高的下属依次提升,直到成员没有儿子队列(下属)为止
}
p->f->s.remove(p); //左链中最后一个资历最高的没有下属(儿子队列)的成员被提升后,
//将该成员在其父节点中的儿子队列中移除,很绕口,其实就是将它在原先的儿子队列中移除,
//然后将他后面的队列前移,这里用的是list表的函数来操作的
delete p; //将该成员占用的空间释放掉。
}
void print( int dep, Tman *now ) //从处于dep层的节点*now出发,打印树的文本形式
{
if( now == NULL ) return ; //当打印到最后一个成员(没有下属)时,回溯
for( int i = dep - 1; i > 0; i-- ) //打印dep-1 层的'+'
cout << "+";
cout << now->name << endl; //打印当前的成员名
//从当前成员节点开始,依次递归打印dep+1层的所有儿子
for( list<Tman *>::iterator it = now->s.begin();
it != now->s.end(); it++ )
print( dep + 1, *it );
}
int main()
{
string s1, s2;
root = new Tman(); //动态开辟Tman结构体类存储空间并执行构造函数;
cin >> s2; //输入并存储CEO的名字
hash[s2] = root;
root->name = s2;
while( cin >> s1 ) //循环判断输入的字符串命令
{
if( s1 == "fire" )
{
cin >> s2;
fire( s2 );
}
else if( s1 == "print" )
{
print( 1, root );
for( int i = 0; i < 60; i++ )
cout << '-';
cout << endl;
}
else
{
cin >> s2; //空读hires,将其略过
cin >> s2;
hires( s1, s2 );
}
}
return 0;
}