树形 DP
在学习本章前请确认你已经学习了 动态规划部分简介
树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。
例题¶
以下面这道题为例,介绍一下树形 DP 的一般过程。
例题洛谷 P1352 没有上司的舞会
某大学有 n 个职员,编号为 1\text{~} N 。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 a_i ,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
我们可以定义 f(i,0/1) 代表以 i 为根的子树的最优解(第二维的值为 0 代表 i 不参加舞会的情况,1 代表 i 参加舞会的情况)。
显然,我们可以推出下面两个状态转移方程(其中下面的 x 都是 i 的儿子):
- f(i,0) = \sum\max \{f(x,1),f(x,0)\} (上司不参加舞会时,下属可以参加,也可以不参加)
- f(i,1) = \sum{f(x,0)} + a_i (上司参加舞会时,下属都不会参加)
我们可以通过 DFS,在返回上一层时更新当前节点的最优解。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #include <algorithm> #include <cstdio> using namespace std; struct edge { int v, next; } e[6005]; int head[6005], n, cnt, f[6005][2], ans, is_h[6005], vis[6005]; void addedge(int u, int v) { e[++cnt].v = v; e[cnt].next = head[u]; head[u] = cnt; } void calc(int k) { vis[k] = 1; for (int i = head[k]; i; i = e[i].next) //枚举该节点的每个子节点 { if (vis[e[i].v]) continue; calc(e[i].v); f[k][1] += f[e[i].v][0]; f[k][0] += max(f[e[i].v][0], f[e[i].v][1]); } return; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &f[i][1]); for (int i = 1; i < n; i++) { int l, k; scanf("%d%d", &l, &k); is_h[l] = 1; addedge(k, l); } for (int i = 1; i <= n; i++) if (!is_h[i]) //从根节点开始DFS { calc(i); printf("%d", max(f[i][1], f[i][0])); return 0; } } |
习题¶
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用