树形dp
前言
本文介绍树形dp比较难的状态设计和考虑方式
边贡献型
贡献放置到边上
P3177 [HAOI2015] 树上染色
题意
有一棵点数为 n 的树,树边有边权。给你一个在 0∼n 之内的正整数 k ,你要在这棵树中选择 k 个点,将其染成黑色,并将其他的 n−k 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的收益。问收益最大值是多少。
思路
- 设dp[u][j]表示以u为根的子树中,选j个黑色点,获取到的最大价值(这个价值只是在子树内部的价值,子树外部的价值不算),答案是dp[root][k]
- 可能现在还不理解1,那么看转移方程:
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]+val),这个val是v子树中选k个点,u和v直连边可以贡献的价值,这个价值很好算,看代码即可。 - 代码中计算val没有用到j,只是用到了k,那可能会有疑问,为什么要这样设计j这一维度呢??因为可以转移,背下来即可。
- 转移的正确性:我们说v是u的儿子,但实际上,我们在进行合并操作,因为当利用v计算u的时候,v并没有看成在u中,所以是合并操作。这样第3条就解释通了:我们需要将u”子树“”和v”子树”合并,而合并的之前,u,v内部如何选择黑色点是毫无关系的,而且获取到的最大价值只在自己子树内部,而且已经被统计好。dp的含义是内部边的价值,所以需要j那一维度。
- 注意代码中状态计算的方式,只有这种方式才可以保证复杂度是$O(n^2)$
代码
1 | |
牛客多校Tree
题意
给定一棵树,每个点选择黑、白有对应的代价,定义一棵树的收益为所有黑白点对间路径边权最大值的和,问如何选择每个点的颜色使得收益-代价最大?
思路
- 因为边权最大值才可以算贡献,所以建立kruskal重构树,这样边的贡献只会在子树内部,所以可以用树形dp做
- 设dp[u][j]表示以u为根的子树中选j个黑色点的最大价值,此时价值只会统计到u子树的内部
- 重构树是二叉堆,这样很好的诠释了“合并”的思想,而且,我们只需要知道合并后的那颗树的信息,因为只有这样的信息,才会被后续用到。
- 上一题,v一旦被合并到u中,v的信息就再也用不到了,实际上我们也可以将u的信息合并到v中,但是不方便,因为我们还要计算u与它的father的边的贡献。
- 注意代码中状态计算的方式,只有这种方式才可以保证复杂度是$O(n^2)$
代码
1 | |
所有边的贡献必须在子树内部才可以用这种方法,尤其注意第一种,为什么要那样设计状态
树形dp
http://example.com/2023/11/29/算法/树形dp/