没有上司的舞会 CH5401(树形DP)

题目:
Ural大学有N名职员,编号为1~N。
他们的关系就像一棵以校长为根的树,父节点就是子节点的直接上司。
每个职员有一个快乐指数,用整数 Hi 给出,其中 1≤i≤N。
现在要召开一场周年庆宴会,不过,没有职员愿意和直接上司一起参会。
在满足这个条件的前提下,主办方希望邀请一部分职员参会,使得所有参会职员的快乐指数总和最大,求这个最大值。
输入格式
第一行一个整数N。
接下来N行,第 i 行表示 i 号职员的快乐指数Hi。
接下来N-1行,每行输入一对整数L, K,表示K是L的直接上司。
最后一行输入0,0。
输出格式
输出最大的快乐指数。
数据范围
1≤N≤6000,
−128≤Hi≤127
输入样例:
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0
输出样例:
5
算法思想:这是在树上的动归,一般就以节点从深到浅(子树从小到大)的顺序作为DP的“阶段”。
DP的状态表示中,第一维通常是节点的编号(代表以该节点为根的子树)。大多数时候,我们采用递归的方式实现树形动态规划。对于每个节点x,先递归在它的每个子节点上进行DP,在回溯时,从子节点向节点X进行状态转移。

该题我们以节点编号(子树的根)作为DP状态的第一维。因为一名职员是否愿意参加只跟他直接上司是否参加有关。我们在每棵子树递归完成时,保留两个“代表信息”,一个是根节点参加时整棵子树的最大快乐子树和,而是根节点不参加时整棵子树的最大快乐子树和。
设F[x,0]表示从以x为根的子树中邀请一部分职员参会,并且x不参加舞会时,快乐指数总和的最大值。此时x的子节点(直接下属)可以参会,也可以不参会。
F[x,0]=sum(max(F[s,0],F[s,1]) s是x的子节点
F[x,1]=H[x]+F[s,0] s是x的子节点

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
vector<int> son[10010];
int f[10010][2], v[10010], h[10010], n;

void dp(int x) {
    f[x][0] = 0;
 f[x][1] = h[x];
 for (int i = 0; i < son[x].size(); i++) {
  int y = son[x][i];
  dp(y);
  f[x][0] += max(f[y][0], f[y][1]);
  f[x][1] += f[y][0];
 }
}
int main() {
 cin >> n;
 for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
 for (int i = 1; i < n; i++) {
  int x, y;
  scanf("%d %d", &x, &y);
  v[x] = 1; // x has a father
  son[y].push_back(x); // x is a son of y
 }
 int root;
 for (int i = 1; i <= n; i++)
  if (!v[i]) { // i doesn't have a father
            root = i;
            break;
        }
 dp(root);
 cout << max(f[root][0], f[root][1]) << endl;
}

vector集合补充参看"动态规划详解"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值