树形dp和他的一些板子介绍

说来惭愧,我到现在还不会树形 DP……
树形 DP 是什么?
他就是一种 DP,只不过是搬到了树上。求解树上一个点的答案可以从他的儿子们 DP 转移过来时,那么使用树形 DP 是可行的。
树形 DP 一般是首先 DFS 到叶子节点,然后再从叶子节点一层层递归上来,得到答案。
而树形 DP 的最大难点就是如何设状态和转移方程。
那我们先来看几道例题来入门树形 DP。
P2015
树上背包。
容易发现这棵树如果是一个链的话,那么就非常好 DP。但是他现在是在树上。怎么设状态呢?
我们设
每一个点是怎么转移过来的?想象一个点,他有一堆儿子,求解一个点的最终答案就相当于是在一堆儿子里面分配边数:第一个儿子留多少条边,第二个儿子留多少条边,然后一直这样下去。
这样我们就可以一个儿子一个儿子的转移:遍历儿子,然后转移这个式子:
用代码就这样表示:
1 | for(int i=h[x];i;i=edge[i].nxt){ |
这里解释一下:
这里优化了一下:
的范围变成了 ,因为这一个状态是基于前一个状态,而这个玩意只有二维,所以 必须倒序遍历,以防重复选中。 的范围变成了 ,因为 是之前的加上现在选的一共 条边。然后从极端条件上来看,边全选这个儿子的,又因为从这个点到他的儿子连了一条边,所以就只能选 条边。并且你还要考虑到儿子的子树的边数,所以要取 min。
总体代码:
1 |
|
P1352
这个也是树形 DP,这个的 DP 状态非常好设,就是设
- 第二维为
就是不选该点,那么他的儿子可以选也可以不选,就在这两个状态里取 max。 - 第二维为
就是选了该点,那么他的儿子只能不选,就直接等于 。
但是因为一个点有多个儿子,所以这个状态的值就是每一个儿子的答案加起来。
最后取
P.S. 这道题其实就是求「最大权值 独立集」的权值。从题意就可以看出来。所以实际上,不需要求根。随便选一个根都可以求出答案。
P2016
和上一道题思路相似。
设
如果当前点没有放士兵,那么他的儿子必须全部放士兵,否则这个点到这个点的儿子的这条边就会没有被观测。所以
。 如果当前点放了士兵,那么他的儿子有没有放士兵就不用管了。所以
。
然后因为所有点都可以变成根,所以 DFS 完了之后直接输出
P2014
这道题还是用树形 DP 的那些套路:这个也是树上背包。
设
这个就默认的是这个必选,所以循环的时候就要写到 i>=2
就行了。
这个的转移方程和之前的也很像:
P1273
这道题也是一个树形 DP,转移方式和之前相似。看了前面几道题应该就会转移了。
只需注意几个点:
- 将
memset
为负无穷大。 - 再把
设为 ,因为不选就是 。 - 然后算答案就把人数
从大到小枚举,如果 ,就直接输出 即可。
P1272
这道题的树形 DP 比较不容易发现:
我们和之前一样,设
然后可以像之前一样得出转移方程:
注意到这里的 +1。
因为你枚举
然后另一种就是之前的儿子加上根节点留
为什么
也是:注意 memset 初始值!!!
P1453
这道题要把环转化成树。
我们用并查集来维护连通块。如果有环的话就会在加点前就会发现。
因为最开始这个环显而易见的,是一条链。而当这条链首尾相连时,就会变为环。所以在加点前,判一下如果 find(u) == find(v)
,那就说明成环了,然后就可以拆环了。
拆环,就相当于是只要上面那个条件成立,那么就不加这条边。设这个时候的
给定一棵树,这棵树的点
是不是很熟悉?这就是ext-P1352!!!求最大权值独立集的权值。
不过这道题要转化一下:
因为点
- Title: 树形dp和他的一些板子介绍
- Author: 11490DX
- Created at : 2024-11-10 21:33:51
- Updated at : 2025-02-14 13:04:59
- Link: https://11490dx.net/2024/11/10/dp-tree/
- License: This work is licensed under CC BY-NC-SA 4.0.