题目链接:点击这里
树形DP介绍:
- 给定一棵有 N 个节点的树(通常是无根树,也就是有 N−1 条无向边),我们可以任选一个节点为根节点,从而定义出每个节点的深度和每棵子树的根。
- 在树上设计动态规划算法时,一般就以节点从深到浅(子树从小到大)的顺序作为DP的“阶段”。
- DP的状态表示中,第一维通常是节点编号(代表以该节点为根的子树)。
- 大多数时候,我们采用递归的方式实现树形动态规划。对于每个节点 x,先递归在它的每个子节点上进行DP,在回溯时,从子节点向节点 x 进行状态转移。
本题思路分析:
以节点编号(子树的根)作为DP状态的第一维。
因为一名职员是否愿意参加只跟他的直接上司是否参加有关,所以我们在每棵子树递归完成时,保留两个“代表信息":
- 根节点参加时整棵子树的最大快乐指数和
- 根节点不参加时整棵子树的最大快乐指数和
设 F[x,0] 表示从以 x 为根的子树中邀请一部分职员参会, 并且 x 不参加舞会时,快乐指数总和的最大值。此时,x 的子节点 s(直接下属)可以参会,也可以不参会。
F[x,0]=max(F[s,0],F[s,1])
设 F[x,1] 表示从以 x 为根的子树中邀请一部分职员参会, 并且 x 参加舞会时,快乐指数总和的最大值。此时,x 的所有子节点 s(直接下属)都不能参会。
F[x,1]=H[x]+F[s,0]
本题输入的是一棵有根树(指定了节点间的上下属关系),故我们需要先找出没有上司的节点 root 作为根,DP的目标为 max(F[root,0],F[root,1])。时间复杂度 O(N)。
#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;
}
菜是原罪QAQ
发布了740 篇原创文章 · 获赞 112 · 访问量 13万+
关注