问题描述:
Stewart教授是一家公司总裁的顾问,这家公司计划一个公司聚会。这个公司有一个层次式的结构;也就是说,管理关系形成一棵以总裁为根的树。人事部给每个雇员以喜欢聚会的程度来排名,这是个实数。为了使每个参加者都喜欢这个聚会,总裁不希望一个雇员和他(她)的直接上司同时参加。
Stewart教授面对一棵描述公司结构的树,使用了左子女、右兄弟表示法。树中每个结点除了包含指针,还包含雇员的名字和该雇员喜欢聚会的排名。描述一个算法,它生成一张客人列表,使得客人喜欢聚会的程度的总和最大。分析你的算法的执行时间。
解答:这题目很像背包问题中的依赖背包问题,公司的每个成员都有去或不去两个状态,我们0表示去,1表示不去,而每个状态的选择又会影响到它孩子节点的状态,假设公司员工的树图符合下面这个图:

else R[N.0]>R[N.1]?N.state=0:N.state=1 父节点去,孩子可去可不去
然后对每个N如果有孩子,则递归地遍历他每一个孩子。因为总的时间为两次遍历的时间O(2n)
第一遍用备忘录版本的递归即可。伪代码如下:
假设结点定义为:
struct Node{
string key; //结点标识,也即用来区分每个人
Node *leftchild;
Node *rightbro;
Node *parent;
int L; //表示当前结点对聚会的热衷程度
};
那么备忘录版本的动规伪代码如下:
hash_map<string> R; //表示以key+tag(tag即为0或1表示去或不去的状态)的根节点最大喜好度
//初始所有的R[key]=INT_MIN(也就是无穷小)
int PARTY(Node *curr, int tag){
if(curr==NULL) return 0;
string key=curr->key+tag?"1":"0";
if(curr->leftchild==NULL) R[key]=tag?curr->L:0; //表示当前节点没有孩子
if(R[key]>INT_MIN) return R[key];
if(tag){ //表示当前节点去聚会,那么其子节点全不能去
int sum=0;
Node *beg=curr->leftchild;
while(beg){
sum+=PARTY(beg,0);
beg=beg->rightbro;
}
sum+=curr->L; //加上当前节点的喜好度
R[key]=sum;
}else{
int max=0;
Node *beg=curr->leftchild;
while(beg){
int max_0=PARTY(beg,0);
int max_1=PARTY(beg,1);
max+=max_0>max_1:max_0:max_1;
beg=beg->rightbro;
}
R[key]=max;
}
return R[key];
}
最终max{R[root->key+"0"], R[root->key+"1"]}就是最终的值
下面再遍历一遍所有结点来确定每个人去或留的状态,即可得到list.

本文介绍了一个基于树形结构的算法,用于解决公司聚会时如何选择参与者的问题,目标是最大化参与者对聚会的喜爱程度总和,同时避免上下级同时参加的情况。
3647

被折叠的 条评论
为什么被折叠?



