DP做题笔记
LG3698 小Q的棋盘
- 贪心
这是一开始并没有想到的方法。考虑钦定一条起始于根节点的链,在这条路径上的节点每次对答案造成贡献仅仅需要一步。而在这条链之外的节点要想对答案造成贡献,就需要花费两步。所以就有一个贪心的思路——走最长链。
- DP
设 \(f_{u,j,0/1}\) 表示在 \(u\) 号节点走了 \(j\) 步,是否需要回到该节点
LG3047 Nearby Cows G
树形dp。
当一遍dfs不好解决问题时,考虑最初的dp需要什么条件,也许多次dp和使用辅助的dp数组会使得问题豁然开朗。
对于这一题,求出一个点的答案需要其父节点和子节点的信息。
设 \(f_{i,j}\) 表示原问题所求的答案,即在节点 \(i\) ,距离 \(j\) 的节点价值。
然后就会发现这个并不是那么的好求,但是子节点的贡献价值是好求的。
于是设 \(d_{i,j}\) 表示在节点 \(i\) ,距离 \(j\) 的子节点价值。
LG2585 三色二叉树
树形dp。
先递归一遍求解出树的结构,然后用dp求解即可。
设 \(f_{u,0/1/2}\) 表示在节点 \(u\) 被染成三种颜色之后子树内的最大值。
根据儿子的个数分情况讨论。
最小值同理。
LG3177 树上染色
一个比较普通的树上背包。
注意树上背包的复杂度是 \(O(NK)\) 的。
树上背包复杂度证明
LG3092 No Change G
设 \(f_S\) 表示在 \(S\) 状态时的最大步数。
注意到 \(f_S\) 必定由小于 \(S\) 的状态转移而来,因此就可以简单地进行枚举状态利用二分进行状态转移。
但是写的时候nt了,死磕在按照 \(i\) 的顺序去枚举转移,为了不 TLE 还套了一个单调队列上去,导致时间复杂度 \(O(nlog^2_n)\),编写难度也增加了不少。
LG2167 Bill的挑战
设 \(f_{i,S}\) 表示在第 \(i\) 位时当前刚好匹配的字符串集合是 \(S\)。
那么根据当前匹配集合内 \(i+1\) 位枚举匹配的字符即可转移。
借助一个 \(mask_{i,j}\) 数组,表示当前第 \(i\) 位的 第 \(j\) 个字符串能够匹配的最大字符串集合,会更方便转移。
注意全部是问号且不与其他字符串匹配的集合要进行特殊的一次转移。
LG1081 开车旅行
\(f_{i,0/1,k}\) 存储从 \(i\) 号节点,\(A/B\) 先开车,行驶了 \(2^k\) 天的数据。
我们需要维护的数据有在该状态时 \(A/B\) 走的路程和该状态之后到达的城市,用结构体存储即可。
先预处理 \(f_{i,0/1,0}\),使用 \(set\) 维护,寻找比当前的 \(H\) 值前二大和前二小的城市,再在其中找到第一和第二个,会比较好写。
然后就是正常的倍增DP的思路去做就可以了。
LG5468 [NOI2019]回家路线
将每一趟火车当作一条路径。
设 \(f_i\) 表示走完第 \(i\) 条路径的时间最小值(去掉 \(q_{s_k}\))。
假设 \(i\) 能从 \(j\) 转移过来,于是就有状态转移方程:
整理一下
注意到这是一个典型的斜率优化 DP 的形式。
但在斜率优化之前,我们还需要考虑一下 \(j\) 的转移范围。
- 转移条件1:\(y_{s_j}=x_{s_{j+1}}\)
只需要创建 \(n\) 个 deque,对每个点分别进行斜率优化即可。 - 转移条件2:\(q_{s_j}\leq p_{s_{j+1}}\)
考虑将一条路径分裂成两个部分加入同一个数组并升序排序,一个以 \(p_i\) 为 \(key\) 值,用于转移 \(i\) 的状态,另一个以 \(q_i\) 为 \(key\) 值,用于加入 deque 中等待被转移。这样就可以保证每次转移状态的时候每一个可供选择的转移都是合法的,不需要考虑这一转移状态。
处理完上述约束之后,这题就基本上做完了。
但是在交这题的加强版的时候,碰到了问题——
我们定义路径 \(i\) 的点坐标为 \((2Aq_i,f_i+Aq_i-Bq_j)\)(就是斜率优化中用来维护下凸壳的坐标),下文用 \((x_i,y_i)\) 代替表示。
于是问题就出现了,就是可能会出现相同的 \(x_i\) 。
这对于维护凸壳是有一定影响的,如果出现了两个坐标相同的点位于队首,凸壳就会卡在那里无法弹出。
于是就需要特判一下来避免影响。
有一个比较方便的处理方法就是在判断斜率时加一个等于号,这样就可以把两边都是 0 的情况判掉。
CF1265E Beautiful Mirrors
设 \(f_i\) 表示 \(1\sim i\) 镜子已经说了漂亮,接下来 \(i+1 \sim n\) 号镜子需要的期望步数。
这样就可以从 \(n\) 开始解一个关于 \(f_0\) 的方程。
LG2602 数字计数
首先将一个 \([a,b]\) 的询问拆成 \([0,a-1]\) 和 \([0,b]\),一个比较常见的技巧。
约定数字从高位到低位分别称为第 \(1,2...\) 位。
设 \(f_{i,j}\) 表示第 \(i\) 位,以 \(j\) 数字结尾的方案个数,除去顶着上界的数字方案。
\(ans_i\) 表示 \(i\) 数字的出现次数。
\(cnt_s\) 表示 \(s\) 的位数。
\(S_i\) 表示 \(s\) 第 \(i\) 位的数字。
顶着上界就是对于询问 \([0,s]\) 前面 \(1\sim i\) 位均与 \(s\) 相同的数字。
若 \(j < S_i,f_{i,j}+=1\),即从前面顶着上界跑的数字转移过来。
若 \(i=cnt_s \ || \ j \neq 0,f_{i,j}+=1\),即在第 \(i\) 位新建一个数字的方案。
前面忽略的顶着上界跑的方案,不要忘了加回来,从后往前计算方案书就可以了。
LG2501 数字序列
题目要求将 \(a_i\) 变成单调严格上升,于是可以考虑构造一个数组 \(b\) ,令 \(b_i=a_i-i\) ,这样就可以将约束条件变为单调不降。
- 第一问
求出 \(b\) 的最长不降子序列,就是最多不修改的长度 - 第二问
注意到在两个不降子序列 \(b_i,b_j\) 之间的元素,一定会有一个整数 \(k\) ,使得 \(b_{i+1\sim k}=b_i,b_{k+1\sim j-1}=b_j\) 的方案为修改的最优方案。
LG9361 Contests
比较巧妙的倍增dp。
注意到在一次比赛之中,位置更靠前的数字一定比靠后的数字需要的转变步数更少。因为更靠前的数字可以选择其后的所有数字来进行转化,而更靠后的数字选择更少。
设 \(f_{x,a,b}\) 表示数字 x ,经过 \(2^a\) 个数字的位置转变,最终在 \(b\) 号比赛中能够到达的最前的位置。
设 \(ans\) 为 最大的不能
到达 \(y\) 前面位置的转化步数。
枚举 \(n\) 的二进制位的每一位 \(i\)。
设 \(pre\) 为前面合法的转化步数。
用一个数组 \(tra\) 记录转化 \(pre\) 后的每一组比赛中的最前位置。如果转化 \(pre+2^i\) 步之后在某一场比赛之中的数字比 \(y\) 的位置更靠前,那么这一位就不能加上去,反之则可以,并记录下这一位的影响到 \(tra\) 中。