树和图上DP(动态规划)杂题题解
都是 DP 题就不放代码了,编程难度都不大
Luogu P1352 没有上司的舞会
树形 DP
设 \(f_{i,j}\) 表示考虑到第 \(i\) 个人时,他去(\(j=1\))的答案最大值和不去(\(j=0\))的答案最大值
从叶子节点向树根维护即可
Luogu P1122 最大子树和
树形 DP
先随便找一个点作为根,设 \(f_i\) 表示 \(i\) (必选)及其子树形成的答案的最大值,那么有
由于必须至少选一朵花,答案要记得初始化为负无穷
Luogu P2016 战略游戏
树形 DP
设 \(f_{i,0}\) 表示 \(i\) 不放士兵并且其子树中所有边都满足条件所需要的最少士兵, \(f_{i,1}\) 表示 \(i\) 要放士兵并且其子树中所有边都满足条件所需要的最少士兵
Luogu P2015 二叉苹果树
树形 DP
设 \(f_{i,j}\) 表示考虑到节点 \(i\) 时,子树内保留 \(j\) 条边的最大苹果数,假设一个点 \(i\) 的子树的总边数为 \(\text{size}_i\),从 \(u\) 到 \(v\) 的边上有 \(e_{u,v}\) 个苹果
从叶子节点向树根维护即可,注意至少要留一条边连接 \(i,v\)
Luogu P2014 [CTSC1997] 选课
树上分组背包(博客推荐:分组背包+依赖背包)
首先显然转换为树形结构,以 \(0\) 为根节点(把它作为必选课程,总计选择 \(n+1\) 门课),避免维护森林
设 \(f_{i,j}\) 表示以 \(i\) 为根节点的子树中,选择 \(j\) 门课程能获得的最大学分
以 \(i\) 的子节点为根的每个子树就相当于一组物品,需要从中选一个
需要注意由于使用了滚动数组,在枚举 \(j\) 的时候需要从 \(\text{size}_i\) 到 \(0\) 倒着枚举,还有需要注意必须满足 \(j>k\) 因为至少要留一个位置留给 \(i\)(只有选了 \(i\) 才能选子树内其他节点)
Luogu P1273 有线电视网
树上背包
设 \(f_{i,j}\) 表示以 \(i\) 为根的子树中选择 \(j\) 个人能赚到的最多的钱
其中 \(e_{i,v}\) 表示通过从 \(i\) 到 \(v\) 的边的花费,同样需要注意枚举 \(j\) 需要倒着枚举,同时 \(k\) 要满足小于等于 \(j\)
Luogu P1613 跑路
倍增预处理一下两个点之间走 \(2^k\) 步能否到达,能到达就记 \(\text{dis}_{u,v}=1\) 表示能花费一个单位时间从 \(u\) 到 \(v\)
然后最短路就行,注意要把 \(\text{dis}_{i,i}\) 设置为 \(0\),其他设置为正无穷
Luogu P2656 采蘑菇
缩点+DP 就好,不太懂为什么是蓝题,直接缩点算出每一个强连通分量里能采到的蘑菇总数,然后以强连通分量为节点建立一张新的图,从起点开始简单 DP 一下就好,设 \(f_{i}\) 表示走到强连通分量 \(i\) 能采到的最大蘑菇数量
用强连通分量内的总蘑菇数和连接两个强连通分量的边的首次采集蘑菇数更新就好
Luogu P2585 [ZJOI2006]三色二叉树
直接 DP 就好,设 \(f_{i,0},f_{i,1},f_{i,2}\) 分别表示点 \(i\) 为红、绿、蓝色时以 \(i\) 为根的子树最多有多少个绿色的节点,设 \(g_{i,0},g_{i,1},g_{i,2}\) 分别表示点 \(i\) 为红、绿、蓝色时以 \(i\) 为根的子树至少有多少个绿色的节点,转移就按照题意就行
若 \(i\) 没有子节点,那么
若 \(i\) 只有一个子节点 \(v\),那么
若 \(i\) 有两个子节点 \(u,v\),那么
\(g\) 的转移同理,代码看着恶心其实还好,都是大段复制
Luogu P2515 [HAOI2010]软件安装
不难发现应该根据依赖关系建图,由被依赖的点向依赖的点连边,然后考虑 DP,但是原图上可能存在环,要想办法消掉,显然一个环如果要选就必须全部一起选,容易想到利用 Tarjan 缩点,计算出每个环(强连通分量)的总磁盘空间和总价值,再以算出的强连通分量为节点重新建图
这样做之后新图就是一片森林(原图并不保证联通),为了方便 DP 应该建立一个价值和空间都为 \(0\) 的虚拟点向所有入度为 \(0\) 的节点连边,这样只要以这个虚拟点为根做 DP 就好了
剩下的部分就很简单了,就是一个简单背包,考虑设 \(f_{i,j}\) 表示以 \(i\) 为根的子树中总空间为 \(j\) 时能获得的最大价值,\(v\) 为 \(i\) 的子节点,那么有转移
注意背包老套路,\(j\) 要倒着枚举
边界条件就是 \(\forall s\in[w_i,m],f_{i,s}=v_i\)
Luogu P2986 & USACO10MAR Great Cow Gathering G
换根 DP 模板
对于一个点计算不方便程度是 \(\Theta(n)\) 的,对于每一个点都算一遍就变成了 \(\Theta(n^2)\),考虑到相邻的点有大量重复计算的部分,我们其实可以用某个点已知的贡献 \(\Theta(1)\) 算出与它相邻的点的贡献
假设我们现在已知点 \(u\) 的贡献,\(v\) 是与 \(u\) 相邻的一个点,想一想我们把目标从 \(u\) 转到 \(v\) 需要怎么更改答案
首先树肯定可以转化为如图所示这样,这样看起来也方便理解一点,当目标从 \(u\) 转到 \(v\) 后,所有 \(v\) 后面的节点内的所有牛都可以少走连接 \(u,v\) 这条边的长度,所有 \(u\) 后面的节点内所有的牛都需要多走连接 \(u,v\) 这条边的长度,也就是说我们只需要知道这两坨分别有多少点就可以了
可以通过先随便找一个点为根预处理出以这个点为根每个点的子树大小,这样一坨的点数就是某个点子树的大小,另一坨的点数就可以通过总点数减去这一坨的点数算出来
总结一下就是先预处理一下以某个点为根的子树大小和答案,然后 DFS 从树上往下 DP 就好了
只写了一点点简单的题,可能之后还会再多加点题