学习笔记——树形DP
作者:互联网
前言
学完树形$DP$,$NOIP$会考的所有$DP$我们就都学完了,所以让我们一鼓作气,开始树形$DP$的学习之旅吧!
一、树形$DP$的基本概念
顾名思义,树形$DP$就是在树这种数据结构上进行$DP$(这不是废话吗?),通过有限次遍历树,记录相关信息,以求解问题。因为树形$DP$是建立在树上的,而树中的父子关系本身就是一个递归结构(满足子问题重叠性),所以自然而然地就衍生出了两种$DP$顺序:
1.叶子节点 $\Rightarrow $ 根节点:就是将叶子节点的信息向上传到父亲节点,在父亲节点处进行信息的整合。最后由根节点记录的信息得到最优解。
例如本题可以将下属来不来得到的权值作为信息传递到上级(题解)(我的代码)
2.根节点 $\Rightarrow $ 叶子节点:取所有点作为一次根节点进行求值,此时父节点得到整棵树的信息,只需要去除这个儿子节点$DP$值的影响,然后再转移给这个儿子。(此做法一般不用,但有时可能有奇效)
树形$DP$的顺序:一般按照${\color{Red}\colorbox{White}{后续遍历}}$的顺序(先处理儿子节点再处理当前节点(这样才符合$DP$的三个基本条件))
实现方式:树形$DP$一般用${\color{Red}\colorbox{White}{记忆化搜索}}$实现(既然是记搜当然用的是递归了)(类似于树剖的第一个dfs)
时间复杂度:一般的树形$DP$的时间复杂度是$O(N)$(因为每一个节点只会被遍历一次),如果有附加维$M$,则复杂度为$O(N\times M)$。
二、树形$DP$的几种类型
1.背包类树形$DP$
既然我们前面将背包和树形$DP$联系在一起学了,那我们就先从我们熟悉的背包类入手吧!(这样能增加我们的信心,减少我们对树形$DP$的畏惧心理)
通过本题,我们就可以大致了解树形$DP$的思路--从根节点出发不断向子树进行搜索,再将子树的信息合并到该节点上。(注意:本题要考虑只选该节点与子树连接的这一条边的情况)
本题我们又学会了一种思想--建虚点。因为本题是在森林中选一定的节点(选完父节点才可以选子节点),但我们又不可以只由一棵树得到最优解,所以我们可以考虑建一个虚点($0$),令它向所有树的根节点连一条边,接着只要对以虚点为根节点的子树进行树形$DP$求出最优解即可。
通过本题,我们要学会在树形$DP$时注意细节--该节点与它儿子节点相连的边到底可不可取,以后做树形$DP$题推导状态转移方程时要仔细考虑这个问题,才能更正确的做出树形$DP$题。
4.状态转移方程含义比较新奇的题(内层循环顺序讲解清楚的题解)(我的代码)
本题告诉我们:
(1)树形$DP$的状态转移方程记录的可以不只是一个最优解,还可以记录该状态对最终答案的贡献。
(2)我们可以通过建立双向边的方式将一棵树强行转化为以1号节点为根节点的树,然后通过遍历顺序重新确立父子节点。
(3)我们要注意该状态有没有被更新过,可以通过思考将一些不可能更新答案的状态排除(本题中还是初始值-1的状态),加快程序的运行效率。
先吐槽一下:普及$DP+IOI$输入$=$提高难度
好了好了,来总结一下本题的收获
(1)联想已学:遇到另类的输入时,可以联想一下我们已学算法中有没有类似的结构。例如本题可以用类似线段树的建树方式来解决输入问题。
(2)根据题意合理运用填表法和刷表法:
还是先简单解释一下这两个词吧!
填表法 :就是一般的动态规划,当前点的状态,可以直接用状态方程,根据之前点的状态推导出来。
刷表法:由当前点的状态,更新其他点的状态。
刷表法需要注意:只用于每个状态所依赖的状态对它的影响相互独立。
这么说大家是不是还是一头雾水,举个例子:如果状态$A$对状态$B$有影响,状态$C$也对状态$B$有影响,当状态$A$的影响和状态$C$的影响相互不影响,就可以运用刷表法。
本题中就记录到达该走廊末尾需要花费的时间(设为$tim$),令$j$表示分配给左儿子的时间,$k$表示分配给右儿子的时间,然后更新以$u$为根节点的树花$tim+j+k$秒可以取得的最大价值。这样可以极大地减少思考难度。
继续挖坑 (挖坑太多后面会不会填不完啊)
2.普通树形$DP$
经历过树形背包的洗礼,我们应该了解到了树形$DP$的基本思路,接下来就开始真正的树形$DP$吧!
本题作为树形$DP$的经典例题,当然会在各种场合出现。本题要求父子节点二选一,使总价值最大,自然是一道典型的最大权独立集问题板子。做这类题,我们可以在$DP$数组上多开一维,这一维只有$0$或$1$组成,表示该节点选或者不选,然后在根节点处统计答案最大值即可。
本题要求 在给定的树上取最小的节点数,使所有边都至少有一个端点在选中的集合中。 我们还可以仿照第一问的思路继续解题-- 在$DP$数组上多开一维,表示该节点选或者不选,然后在根节点处统计答案最小权值 。所以,只要我们深刻理解树形$DP$的方法,就可以如法炮制做其他题。
本题看上去像例题2的加强版,但其实本题要求守点,而不是像例题2一样守边。就是这一个看似细小的差距,却导致了代码的千差万别。例如下面这张图:
如果我们只守1、4号点,那这一条链上的点就都可以守到,但2<->3这一条边明显没有守到。所以本题中我们要考虑三种情况:
1.$x$节点被自己覆盖,即选择$x$点来覆盖$x$点
2.$x$节点被儿子$y$覆盖,即选择$y$点来覆盖$x$点
3.$x$节点被父亲$fa$覆盖,即选择$fa$点来覆盖$x$点
然后分类讨论进行状态转移,最后在根节点处取$min(f[1][0],f[1][1])$作为代价的最小值。(因为根节点没有父亲节点,所以不用讨论该情况)
本题想到$DP$的思路并不难,但主要恶心在输出每个点是否可能成为最优解,这就导致一道本来黄$\sim$绿的树形$DP$题强行升紫。
思路:
我们用dp[u][0]
表示以$u$为根节点的子树将所有叶子节点控制的最小代价,用
dp[u][1]
表示以$u$为根节点的子树中还剩一个叶子节点未控制的最小代价。
-
$dp[u][1] = \sum\limits dp[v][0] - max(dp[v][0] - dp[v][1])$(我们可以贪心选择一颗子树,使$dp[u][1]$的代价最小)
-
$dp[u][0] = min(sum[u],dp[u][1] + val[u])$(显然,我们可以不选当前节点取尽$u$的所有子树,也可以选当前节点)
我们再建一堆辅助数组,用于输出答案,用g[u]
表示$u$的儿子节点中dp[v][0]-dp[v][1]
的最大值,用
num[u]
表示dp[u][0]
可以有多少个dp[v][1]
转移而来,用sum[u]
表示所有dp[v][0]
之和,再用can[u][1]
表示可以通过dp[u][1]
转移到其父亲节点can[u][0]
表示可以通过dp[u][0]
转移到其父亲节点。
-
如果$can[u][0]=1$
-
$\circ$转移到$can[v][0]$
-
$\circ$ $\circ$ 如果$num[u]>1$,则$can[v][0]=1$,因为$v$节点不一定传递到$dp[u][1]$
-
$\circ$ $\circ$ 如果$g[u]!=dp[v][0]-dp[v][1]$,即$v$节点一定传递到$dp[u][0]$,$can[v][0]=1$。
-
$\circ$ $\circ$ 如果$dp[u][0]==sum[u]$,即$v$节点一定传递到$dp[u][0]$,$can[v][0]=1$。
-
$\circ$转移到$can[v][1]$
-
$\circ$ $\circ$ 如果$sum[u]-dp[v][0]+dp[v][1]+val[u]==dp[u][0]$,即贪心选择的子树是$v$节点,显然$can[v][1]=1$。
-
如果$can[u][1]=1$
-
$\circ$转移到$can[v][0]$
-
$\circ$ $\circ$ 如果$num[u]>1$,则$can[v][0]=1$,因为$v$节点不一定传递到$dp[u][1]$
-
$\circ$ $\circ$ 如果$g[u]!=dp[v][0]-dp[v][1]$,即$v$节点一定传递到$dp[u][0]$,$can[v][0]=1$。
-
$\circ$转移到$can[v][1]$
-
$\circ$ $\circ$ 如果$g[u]==dp[v][0]-dp[v][1]$,即贪心选择的子树是$v$节点,显然$can[v][1]=1$。
又到了扔题时间
1.P4084 [USACO17DEC]Barn Painting G
4.P3574 [POI2014]FAR-FarmCraft
3.换根树形$DP$
1.基本思路
-
先以$1$号节点,进行一遍$dfs$,令$f[u]$表示以$u$为根节点的子树的最优解。
-
将根节点转移到其儿子节点上,并不断更新$f[to]$(假设$to$为$u$的儿子节点)
-
将第二步推而广之,逐渐更新完整颗树
由上面的操作可以看出,换根$DP$其实就是不断利用以$1$为根节点得到的信息更新以其他点为根节点得到的信息。
1.来一道入门题--找使所有结点的深度之和最大的根节点(题解)(我的代码)
作为入门题,当然是要让我们熟悉换根$DP$基本思路的题。本题可以令$f[i]$表示以$i$号节点为根节点的树的深度和,用根节点从$u$移动到$to$($u$的儿子节点)时以$to$为根节点这颗子树的所有节点的深度$-1$,而其他节点的深度$+1$进行状态转移。
一样的板子和套路,不过因为极大值比较大,所以先以$f[1]$为极大值再更新其他值即可。(白白WA40pts)
本题思路比较好想(改造该点子树中最大节点数不超过$\frac{n}{2}$的子树),但是码代码时一定要养成好习惯,不要打出int to=e[i].nxt
这样的细节错误 (关键这种错误找了一个下午+一个晚上才找出来) 。
因为要去考$NOIP$,所以还是挖坑 (估计也不会填了,毕竟考完还要补文化)
我回来填坑了$!$
1.P6419 [COCI2014-2015#1] Kamp(题解)(我的代码)
2.P3647 [APIO2014]连珠线(题解)(我的代码)
标签:DP,笔记,circ,树形,dp,节点,本题 来源: https://www.cnblogs.com/wujianxiang/p/15103725.html