tyvj1052 没有上司的舞会 【例题精讲】

题目描述

 

某大学有N个职员,编号为1~N。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。

 

输入输出格式

输入格式:

 

第一行一个整数N。(1<=N<=6000)

 

接下来N行,第i+1行表示i号职员的快乐指数Ri。(-128<=Ri<=127)

 

接下来N-1行,每行输入一对整数L,K。表示K是L的直接上司。

 

最后一行输入0 0

 

输出格式:

 

输出最大的快乐指数。

 
      这道题解法为动态规划,但是和一般的线性dp有所不同,这道题是在一棵树形结构上执行动态规划,对于这样的动态规划,我们称其为树形dp,在这种dp中,一般我们把节点(子树)作为动态规划的 阶段。然后节点体现在状态数组的 第一维。另外,树形dp的过程往往是用 递归形式实现的,因为我们更新节点的顺序是从深到浅,然后一般我浅的地方的状态还没有更新,怎么办,当然就要一层一层地去递归先去更新那个最浅的地方!
      现在我们开始说这道题的内容,因为上司和下属共同形成了一棵有根树,并且相邻的两层不能同时出席舞会。
      所以我们可以定义f[x][0]表示当x节点出席舞会时,整棵子树能获得的最大价值,当然,x的所有直接下属都不能参加舞会。
      定义f[x][1]为当x不出席舞会时,整棵子树能获得的最大价值,当然,他的直接下属既可以参加又可以不参加。
      这样,就可以有一个初步的动态规划思路,阶段便是节点,但是这里我们把原本应该是最外层循环的阶段写成递归,为的就是更方便的访问节点,并且思路更清晰;
      然后面临的一个问题,如何寻找校长节点?
      我们可以在输入时记录,有父节点的点一定不是校长,最后循环去排除,也可以用类似拓扑的办法。
 
 
下面上代码!
 
 1 //Is mule is horse please pull out and six six.--Luo
 2 #include<iostream>
 3 #include<cmath>
 4 #include<cstdlib>
 5 #include<cstdio>
 6 #include<cstring>
 7 #include<string>
 8 #include<algorithm>
 9 #include<vector> 
10 using namespace std;
11 const int MAXN=6001;
12 int hp[MAXN];//happy 快乐指数; 
13 int f[MAXN][2]={0};//f[x][0]代表x不参加时它的子树能合成的最大价值;
14 //f[x][1]则表示x参加时它子树获得的最大价值; 
15 bool notdaboss[MAXN]={0}; 
16 int z,x,c,v,a,b,n,m;
17 vector<int>boss[MAXN];//存储树形结构; 
18 //在树形dp中,一般把dp写成递归的形式; 
19 int dfs(int now,bool tpi)//和f数组差不多; 
20 {
21     if(f[now][tpi]) return f[now][tpi];//记忆 
22     if(!boss[now].size()) return hp[now]*tpi;//如果没有子树,那就是叶子结点,可以直接返回相应值; 
23     int ans=0;
24     if(tpi)
25     {
26         for(int i=0;i<boss[now].size();i++)//状态
27         {
28             int &next=boss[now][i];
29             ans+=dfs(next,0);//如果根节点参加会议,那直接子节点一定不参加; 
30         }
31     }
32     else
33     {
34         for(int i=0;i<boss[now].size();i++)//状态
35         {
36             int &next=boss[now][i];
37             ans+=max(dfs(next,true),dfs(next,false));//如果这个节点不参加,那么子节点既可以参加又可以不参加; 
38         }
39     }
40     return f[now][tpi]=tpi? ans+hp[now]:ans;//选不选的决策问题! 
41 }
42 int main()
43 {
44     scanf("%d",&n);
45     for(int i=1;i<=n;i++)
46     {
47         scanf("%d",&hp[i]);
48     }
49     while(scanf("%d%d",&a,&b)&&a&&b)
50     {
51         boss[b].push_back(a);
52         notdaboss[a]=true;//寻找根节点 
53     }
54     for(int i=1;i<=n;i++)
55     {
56         if(!notdaboss[i])
57         {
58             m=i;break;//寻找根节点(类似于拓扑) 
59         }
60     }
61     cout<<max(dfs(m,0),dfs(m,1));
62     return 0;
63 }
View Code

 

 

转载于:https://www.cnblogs.com/Alan-Luo/articles/8723304.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值