0.定义
顾名思义就是在树上的 DP。
1.转移方式
这种 DP 不能用普通的方式转移,一般是通过 DFS 来实现转移,因为他的状态转移方程一般和子结点有关,所以可以计算完子结点的答案后合并到父结点。
一般来说,答案是根结点的答案
但是,这个树不一定是有根树
所以有些时候得扫一遍,求出以每个结点为根的答案。
题型很多,基本可以和其他任何一种 DP 融合起来。
2.类型 & 例题
1.朴素版本
对于一条边,有两种决策:保留或者计入子树答案
所以,有 DP 方程:
意思就是,看是否保留子树的答案,如果子树答案小于 ,则一定劣于不选该子树,所以可以这么写。
2.树上背包(有依赖的背包)
填了 24.11.19 背包 DP 的坑
其实就是在树上的分组背包,模板如下
void DP(int u,int fa){ for(auto v:g[u]) { if(v == fa) continue; DP(v,u); for(int j = m;j >= 0;j--) { for(int k = 0;k <= j;k++) dp[u][j] = max(dp[u][j],dp[u][j - k] + dp[v][k] + val); } }}上述代码中, 表示给以这个结点为根的子树分配多少容量, 表示给该结点的子结点为根的子树分配多少容量。
可以选子树中的边的充分条件是选择该条边,所以说上面的 只能遍历到 。同时,转移方程为 。
所以 DP 转移代码如下:
void DP(int u,int fa){ for(auto x:g[u]) { int v = x.first; if(v == fa) continue; DP(v,u); for(int j = m;j >= 1;j--) { for(int k = 0;k < j;k++) dp[u][j] = max(dp[u][j],dp[u][j - k - 1] + dp[v][k] + x.second); } }}3.换根DP
就是根结点的修改会导致一些变化。
首先,我们需要一遍 DFS 来处理子树大小。
然后,令 为当以 为根时,结点的深度和。
所以,有 DP 方程:
因为:
- 在 的子树上的结点深度减少 ,深度和减少了
- 不在 的子树上的结点深度增加了 ,则深度和减少了
所以最后再扫一一遍就可以求出答案了。
我觉得就差不多了
Thanks for reading!