树形 DP 学习笔记(一)

· · 算法·理论

前言

本文讲解如何用树形 DP 解决兄弟节点间没有数量上的约束关系的树上问题。状态通常是 dp_i 表示以 i 为根节点的子树的信息。

一般是将各个子树的信息合并至根节点的信息,故一般先递归再转移。

这种树形 DP 较为简单,写几道例题看一看。

P1122 最大子树和

dp_x 表示以 x 为根的子树的最大子树和。对于边 x\rightarrow to,其决策是以 x 为根的子树选不选以 to 为根的子树中的最大子树和。故有转移 dp[x]=max(dp[x],dp[x]+dp[to]);

P1352 没有上司的舞会

经典树形 DP 入门题。

发现每个点选或不选会影响其子孙节点的决策,故考虑升维。

```cpp dp[x][0]+=max(dp[to][0],dp[to][1]); dp[x][1]+=dp[to][0]; ``` ### [P2585 [ZJOI2006] 三色二叉树](https://www.luogu.com.cn/problem/P2585) 状态转移很套路,设二维就可以了,一维是根节点,二维是染的色,可以将颜色绿、红、蓝分别设为数字 $\texttt{0/1/2}$ 进行转移。 主要是谈谈怎么建树。可以直接递归建树,因为题目给出的输入顺序是按照 DFS 序给出的,所以我们可以顺其自然,一边递归一边建树。如下: ```cpp int build(){ int cur=++tot; if(s[cur]=='2'){ ls[cur]=build(); rs[cur]=build(); } else if(s[cur]=='1') ls[cur]=build(); return cur; } ``` ### [P1131 [ZJOI2007] 时态同步](https://www.luogu.com.cn/problem/P1131) $dp_x$ 表示以 $x$ 为根的子树时态同步所需要的代价。我们需要先找到这棵子树中最长的距离,然后再调整其他的距离。但最长的距离也需要维护,故进行 $2$ 遍 DFS。一遍求最长距离,一遍转移。 部分代码: ```cpp dis[x]=max(dis[x],dis[to]+w);//以x为根节点的子树中的最长链 dp[x]+=dis[x]-dis[to]-w;//将其它链调整至最长链的长度 ``` 怎么感觉写的有点少……溜了溜了。