例题:https://www.luogu.com.cn/problem/P1352

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

  • 我们可以定义 F[i][0/1] 代表以 i 为根的子树的最优解(第二维的值为 0 代表 不参加舞会的情况,1 代表 参加舞会的情况)。

  • 显然,我们可以推出下面两个状态转移方程(其中下面的 x 都是 i 的儿子):
    • 树形DP - 图1(上司不参加舞会时,下属可以参加,也可以不参加)
    • 树形DP - 图2(上司参加舞会时,下属都不会参加)

我们可以通过 DFS,在返回上一层时更新当前结点的最优解。
代码

  1. #include <algorithm>
  2. #include <cstdio>
  3. using namespace std;
  4. struct edge {
  5. int v, next;
  6. } e[6005];
  7. int head[6005], n, cnt, f[6005][2], ans, is_h[6005], vis[6005];
  8. void addedge(int u, int v) { //建图
  9. e[++cnt].v = v;
  10. e[cnt].next = head[u];
  11. head[u] = cnt;
  12. }
  13. void calc(int k) {
  14. vis[k] = 1;
  15. for (int i = head[k]; i; i = e[i].next) { // 枚举该结点的每个子结点
  16. if (vis[e[i].v]) continue;
  17. calc(e[i].v);
  18. f[k][1] += f[e[i].v][0];
  19. f[k][0] += max(f[e[i].v][0], f[e[i].v][1]); //转移方程
  20. }
  21. return;
  22. }
  23. int main() {
  24. scanf("%d", &n);
  25. for (int i = 1; i <= n; i++) scanf("%d", &f[i][1]);
  26. for (int i = 1; i < n; i++) {
  27. int l, k;
  28. scanf("%d%d", &l, &k);
  29. is_h[l] = 1;
  30. addedge(k, l);
  31. }
  32. for (int i = 1; i <= n; i++)
  33. if (!is_h[i]) { // 从根结点开始DFS
  34. calc(i);
  35. printf("%d", max(f[i][1], f[i][0]));
  36. return 0;
  37. }
  38. }