题目传送门最近做的题真是越来越水了(以前没有自己做过树形 DP 题,所以特来水一篇博客)
好了,进入正题。
思路
上司和下属的关系就是一种树形关系,上下级共同构成了一棵树,我们要考虑在这棵树里进行 DP。
由于上司对下属是有影响的(即上司选了下属就不能选了),这是有后效性的吗,所以需要从下属到上司进行 DP,因为下属的选择不会影响到上司的选择。(输入方式已经暗示了一切,因为先输入下属再输入其上司)
用一个二维数组表示状态。dp[v][0/1]
表示到节点 v 所获得的最大快乐指数,0/1 表示是否选择当前点 v。
易推出状态转移方程为 $dp[v][0] = \sum\max(dp[u][0],dp[u][1])$
$dp[v][1] = \sum dp[u][0] + r[v]$(其中 u 是 v 的下属)
这两个方程也很好理解,即上司不去选择下属去不去的快乐指数最大值;上司去时下属不能去,所以只加上上司自己的快乐指数。
最后取所有 dp
数组中的数的最大值就可以了。
写代码时需要从底层向上递推,最底层的点是没有入度的,所以可以维护一个队列,每次加入没有入度的点,枚举其上司,而且在枚举到这个点的子节点时让入度减 1(相当于它的下属已经枚举过了,上司入度要减去 1)
代码
#include <bits/stdc++.h>
using namespace std;
const int maxN = 6005;
struct Edge {
int next, to;
} edge[maxN << 1];
int head[maxN], ha[maxN], indeg[maxN];
int dp[maxN][2];
int cnt, n, ans;
queue<int> que;
void add(int from, int to) {
edge[++cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
}
int main() {
memset(head, -1, sizeof(head));
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &ha[i]);
for (int i = 1; i < n; i++) {
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
indeg[b]++;
}
for (int i = 1; i <= n; i++) {
dp[i][1] = ha[i]; // 直接把快乐指数赋值给 dp[i][1] 了
if (!indeg[i]) que.push(i);
}
while (!que.empty()) {
int u = que.front();
que.pop();
for (int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].to;
dp[v][1] += dp[u][0];
dp[v][0] += max(dp[u][0], dp[u][1]);
indeg[v]--;
if (!indeg[v]) que.push(v);
}
}
for (int i = 1; i <= n; i++)
ans = max(ans, max(dp[i][0], dp[i][1]));
printf("%d\n", ans);
return 0;
}