算法导论--学习笔记010

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

问题描述:

Stewart教授是一家公司总裁的顾问,这家公司计划一个公司聚会。这个公司有一个层次式的结构;也就是说,管理关系形成一棵以总裁为根的树。人事部给每个雇员以喜欢聚会的程度来排名,这是个实数。为了使每个参加者都喜欢这个聚会,总裁不希望一个雇员和他(她)的直接上司同时参加。

Stewart教授面对一棵描述公司结构的树,使用了左子女、右兄弟表示法。树中每个结点除了包含指针,还包含雇员的名字和该雇员喜欢聚会的排名。描述一个算法,它生成一张客人列表,使得客人喜欢聚会的程度的总和最大。分析你的算法的执行时间。

解答:这题目很像背包问题中的依赖背包问题,公司的每个成员都有去或不去两个状态,我们0表示去,1表示不去,而每个状态的选择又会影响到它孩子节点的状态,假设公司员工的树图符合下面这个图:

计划一个公司聚会 - kevinlee_2010 - 云淡风轻
 我们用R[N]表示以N为根节点的子树参加聚会的喜好程度的最大值,用L[N]表示节点N对参加聚会的喜好程度。那么我们知道R[N]就是以下两个值得最大值:
R[N.1]=L[N]+∑ R[N.children.0]    表示节点N参加聚会,那么它所有的孩子都不能参加
R[N.0]=∑ max{R[N.children.1],R[N.children.0]}    表示节点N不参加聚会,那么它所有的孩子可以参加也可以不参加
于是R[N]=max{R[N.1],R[N.0]},哪个值大,就知道了N应不应该去。
在实现过程中我们可以从根节点开始递归遍历一下树,求出其每个节点的R[N.0]和R[N.1],递归结束的条件为到达叶节点N时R[N.0]=0,R[N.1]=L[N],在求出每个节点的R[N.0]和R[N.1]之后,我们递归再遍历一次树,来决定每个节点要不要参加聚会,对于每个内部节点,
如果它没有父节点(也就是根节点)那么设置R[N.0]>R[N.1]?N.state=0:N.state=1
如果是非根节点,首先判断if parent[N].state=1,  N.state=0    父节点去,孩子都不能去

                       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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值