前言
本文主要介绍动态规划的集中进阶模型——树形 DP、DAG 上的 DP、数位 DP。不同于之前的基础模型的一点是,它们的状态并非单纯地建立在线性关系或者二维空间上,而是在树结构,图,二进制数位上进行转移,相对之下可能更麻烦,但其实也是换汤不换药的。
子目录列表
1、树形 DP
2、DAG 上的 DP
3、数位 DP
4.2.3 树形 / DAG / 数位 DP
1、树形 DP
顾名思义,在树结构上进行动态规划,没有太多概念可言,直接以一道经典的例题引入好了。
【没有上司的舞会】某大学有 n 个职员,编号为 1 ~ n 。他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 ai,但是呢,如果某个职员的上司来参加舞会了,那么这个职员就无论如何也不肯来参加舞会了。所以,请你编程计算,邀请哪些职员可以使快乐指数最大,求最大的快乐指数。
在树状结构找子问题其实还是挺方便的,所有节点只和其子节点和唯一父节点相连,利用这点性质来切入:假设以 i 为根的树为一棵子树,如果 i 参加舞会,则其所有子节点均不能参加;如果不参加,则子节点可参加可不参加;再向其所有儿子节点递归,以此类推。
状态定义:f[i] 表示 “以 i 为根节点的子树可以获得的最大快乐指数”;
由于对于任何一个节点又存在两个状态——参加与不参加,所以还需要增加一维 0 / 1,表示是否参加舞会。
状态转移:f[i][0] = ∑max(f[j][0], f[j][1]);
f[i][1] = ∑f[j][0] + a[i],j 表示 i 的儿子节点。
代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 #define MAXN 6005 5 6 struct Edge { 7 int v, nxt; 8 } e[MAXN]; 9 10 int n, o, u, v, tot, f[MAXN][2], nr[MAXN], h[MAXN]; 11 12 void addEdge(int u, int v) { 13 e[++o] = (Edge) {v, h[u]}, h[u] = o; 14 } 15 16 void dfs(int o, int fa) { 17 for (int x = h[o]; x; x = e[x].nxt) { 18 int v = e[x].v; 19 if (v == fa) continue; 20 dfs(v, o); 21 f[o][1] += f[v][0]; 22 f[o][0] += max(f[v][0], f[v][1]); 23 } 24 } 25 26 int main() { 27 cin >> n; 28 for (int i = 1; i <= n; i++) cin >> f[i][1]; 29 for (int i = 1; i <= n; i++) 30 cin >> u >> v, addEdge(v, u), nr[u] = 1; 31 for (int i = 1; i <= n; i++) { 32 if (nr[i]) continue; 33 dfs(i, 0); 34 cout << max(f[i][0], f[i][1]); 35 break; 36 } 37 return 0; 38 }
2、DAG 上的 DP
3、数位 DP