题目链接:点击这里
树形DP介绍:
- 给定一棵有 个节点的树(通常是无根树,也就是有 条无向边),我们可以任选一个节点为根节点,从而定义出每个节点的深度和每棵子树的根。
- 在树上设计动态规划算法时,一般就以节点从深到浅(子树从小到大)的顺序作为DP的“阶段”。
- DP的状态表示中,第一维通常是节点编号(代表以该节点为根的子树)。
- 大多数时候,我们采用递归的方式实现树形动态规划。对于每个节点 ,先递归在它的每个子节点上进行DP,在回溯时,从子节点向节点 进行状态转移。
本题思路分析:
以节点编号(子树的根)作为DP状态的第一维。
因为一名职员是否愿意参加只跟他的直接上司是否参加有关,所以我们在每棵子树递归完成时,保留两个“代表信息":
- 根节点参加时整棵子树的最大快乐指数和
- 根节点不参加时整棵子树的最大快乐指数和
设
表示从以
为根的子树中邀请一部分职员参会, 并且
不参加舞会时,快乐指数总和的最大值。此时,
的子节点
(直接下属)可以参会,也可以不参会。
设
表示从以
为根的子树中邀请一部分职员参会, 并且
参加舞会时,快乐指数总和的最大值。此时,
的所有子节点
(直接下属)都不能参会。
本题输入的是一棵有根树(指定了节点间的上下属关系),故我们需要先找出没有上司的节点 作为根,DP的目标为 。时间复杂度 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 6010;
int n;
int e[N], w[N], ne[N], h[N], idx; //数组模拟邻接表
bool st[N];
int f[N][2];
void add(int u, int v)
{
e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}
void dfs(int u)
{
f[u][0] = 0;
f[u][1] = w[u];
for(int i = h[u]; ~i; i = ne[i]) //枚举当前点的所有边
{
int j = e[i];
dfs(j);
f[u][0] += max(f[j][0], f[j][1]);
f[u][1] += f[j][0];
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d", &w[i]);
memset(h, -1, sizeof h);
for(int i = 1; i <= n - 1; ++i)
{
int a, b;
scanf("%d%d", &a, &b);
add(b, a);
st[a] = true;
}
int root = 1;
while(st[root]) root++; //寻找根节点
dfs(root);
printf("%d", max(f[root][0], f[root][1]));
return 0;
}