AT_dp
教练推荐的DP题单,质量很好。AT链接
A
线性DP。状态:\(f_i\) 表示选到第 \(i\) 个时最优决策。
\(f_{i+1}=\min\{f_{i+1},f_i+|h_i-h_{i+1}|\}\)
\(f_{i+2}=\min\{f_{i+2},f_i+|h_i-h_{i+2}|\}\)
B
上一题相当于这道题 \(k = 2\) 的情况。状态设计同上
转移:$f_i=\min{f_j+|h_i-h_j|} (i-k \leq j \leq i-1) $
C
主要理解DP加维思想。状态: \(f_{i,op}\) 表示第 \(i\) 天干第 \(op\) 件事最大快乐值。
转移:\(f_{i,op}=max\{f_{i-1,opt}\}(opt \neq op)\)
D
01 背包问题的板子。状态: \(f_{i,j}\) 表示考虑到第 \(i\) 个物品,共占用 \(j\) 份空间时的最优解。
转移: \(f_{i,j}=max\{f_{i-1,j},f_{i-1,j-w_i}+v_i \}\)
E
上一题的加强版。观察到 \(v_i\) 的数据范围较小,考虑用 \(v_i\) 作下标。
状态:\(f_{i,j}\) 表示考虑到第 \(i\) 个物品,获得 \(j\) 份价值所需最小代价。初始时 \(f_{1,0}=0\) ,其余均为 \(\inf\)。
转移:\(f_{i,j}=min\{ f_{i-1,j},f_{i-1,j-v_i}+w_i\}\)
结果就是所有 \(f_{n,i}\) 中代价小于 \(W\) 的最大价值。可以将第一维滚掉。
这道题告诉我们,如果约束条件大而答案小,可以把答案放到状态里。
F
线性 DP 经典模型。
设 \(f_{i,j}\) 表示两个子串最后的字符分别是 \(s_i,t_j\) 时的最优结果。
当 \(s_i = t_j\) 时,\(f_{i,j}=f_{i-1,j-1}+1\)
否则,\(f_{i,j}=max\{f_{i-1,j},f_{i,j-1}\}\)。
最后搞两个指针找就行了。
G
拓扑排序,在这个过程中 DP 就行了。
转移:\(f_v=\max\{f_u+1\},Ans=\max\{f_i\}\)。
H
状态:\(f_{i,j}\) 表示走到点 \((i,j)\) 的方案数。
转移:\(f_{i,j}=f_{i-1,j}[i>1][a_{i-1,j}=.]+f_{i,j-1}[j>1][a_{i,j-1}=.]\)。
I
状态:\(f_{i,j}\) 表示前 \(i\) 次中有 \(j\) 次正面朝上的概率。
转移:\(f_{i,j}=f_{i-1,j}\times (1-p_i)+f_{i-1,j-1}\times p_i\)。
J
期望 DP。期望是指 \(\sum p_i \times v_i\),其中 \(p_i\) 指出现概率,\(v_i\) 表示值。
对于这个题,我们先考虑状压,看每个盘子里有多少个苏轼,转移即可,但 \(n \leq 300\),于是我们考虑合并状态。注意到每个装有相同数量苏轼的盘子是等价的,于是我们就可以设 \(f_{i,j,k}\) 表示有 \(i\) 个盘子里苏轼数量为 \(1\),\(j\) 个盘子里苏轼数量为 \(2\),\(k\) 个盘子里苏轼数量为 \(3\),\((n-i-j-k)\) 个盘子里没有苏轼的情况下的答案。于是我们有转移式:
我们发现左右两边都有 \(f_{i,j,k}\),怎么办?移项!得到
于是我们就可以记忆化搜索过掉这个题了。
K
设 \(f_{x}\) 表示还剩 \(x\) 个数的时候 1 有没有可能赢(这个题洛谷上翻译过来的不是很好,但我懒得改了),考虑 \(f_x\) 可以推到 \(f_{x+a_i}\),于是我们有转移方程:
L
正难则反。我们考虑只剩区间 \([i,j]\) 的下一步该怎么走。如果下一步轮到先手走,那么他必然会选 \(\max\{a_{i-1},a_{j+1}\}\),否则他必然会选 \(\min\{a_{i-1},a_{j+1}\}\),设 \(f_{i,j}\) 表示区间 \([i,j]\) 谁赢,转移也就呼之欲出了,答案也就是 \(f_{1,n}\)。
M
观察到 \(n,k\) 都不算大,乘起来也就 \(10^7\),空间时间都不会爆,而无论是 \(O(n)\) 的算法还是 \(O(k)\) 的算法都显得那么无力,因此考虑往 \(O(nk)\) 上靠。
状态: \(f_{i,j}\) 表示前 \(i\) 个孩子分走了 \(j\) 颗糖果的总方案数。
转移: \(f_{i,j}=\sum\limits_{k=0}^{a_i}f_{i-1,j-k}\)。但是硬转是肯定会 TLE 的,因此考虑优化。
优化:观察到 \(\sum\limits_{k=0}^{a_i}f_{i-1,j-k}\) 的结构是连续的一段和,考虑在每一轮 DP 结束后维护 \(f_i\) 的前缀和,这样就可以省去枚举求和的时间,最终复杂度 \(O(nk)\)。
N
区间DP板子。设 \(f_{i,j}\) 是区间 \([i,j]\) 合并的最小代价。
转移;\(f_{i,j}=min\{f_{i,k}+f_{k+1,j}+\sum\limits_{k=i}^j a_k\}\)
O
看到 \(n\leq 21\) 的数据范围,直接想到二进制枚举。但数数题我们先要考虑的是按什么顺序去枚举,我们想到可以让男孩从 \(1\) 到 \(n\) 一个个配,然后枚举集合 \(S\) 表示女孩是否被匹配的状态的集合,设 \(f_{i,S}\) 表示枚举到第 \(i\) 个男孩,女孩集合为 \(S\) 的匹配方案和,转移也就显然。为了节省空间我们可以将 \(i\) 这一位滚掉,答案就是 \(f_{2^n-1}\),时间复杂度 \(O(2^nn)\),空间复杂度 \(O(2^n)\)。
P
没 有 上 司 的 舞 会。
Q
先考虑最朴素的 DP。设 \(f_{i}\) 表示钦定选第 \(i\) 朵花花时的最大价值,有转移 \(f_{i}=\max\{f_{j}[h_j<h_i]\}+a_i\),时间复杂度 \(O(n^2)\)。我们注意到 \(h_i\leq n\),于是我们可以考虑以 \(h_i\) 为下标,\(f_i\) 为值,建一棵线段树,维护单点修改区间 \(\max\) 就过了,时间复杂度 \(O(n\log n)\)。
R
先考虑朴素 DP。设 \(f_{u,v,k}\) 表示 \(u\) 到 \(v\) 距离为 \(k\) 的方案数,我们有转移 \(f_{u,v,k}=\sum_{j} f_{u,j,k-1}\times f_{j,v,1}\)。
观察上面的式子,我们发现 \(f_{j,v,1}\) 就是 \(a_{j,v}\),并且注意到这个形式很像矩阵乘法,于是我们就可以把 \(f\) 除去 \(k\) 的两维看成矩阵,然后矩阵快速幂就可以过了,时间复杂度 \(O(n^3\log n)\)。
一定要先赋值,不然 res 不管怎么乘都是0!
S
数位 DP。设 \(f_{i,j}\) 表示当前考虑到从低到高第 \(i\) 位,得到的结果模 \(d\) 余 \(j\) 的情况总数,我们记忆化搜索的时候还要传一个布尔值参数 \(lim\) 表示当前一位是否受限制。设在满足限制条件下当前位的最大值是 \(mx\),则有递推式:\(f_{i,j}=\sum_{k=0}^{mx} dfs(i-1,(j+i)\bmod d,(i=mx) \cap lim)\),然后特判边界就过了。难点在于理解限制条件的下传。
T
可以自然地想到设 \(f_{i,j}\) 表示前 \(i\) 个数,最有一个是 \(j\) 的方案数,但我们发现这样很难转移,于是我们要尝试去发现一些性质。注意到在一个子序列中,想确定每个数的位置并不需要这个数的具体值,而只需要相对大小。于是我们尝试设 \(f_{i,j}\) 表示前 \(i\) 个数,最后一个数在前 \(i\) 个数中是第 \(j\) 大的方案数。我们就可以容易地得到转移:
然后对于 \(f\) 记前缀和就可以把转移优化到 \(O(1)\) 的时间复杂度了。
U
又是道状压 DP。设 \(f_{S}\) 表示当前完成匹配的集合为 \(S\) 时的最大收益,\(f_S\) 的初值就是 \(S\) 里面所有对的收益和,这一步是 \(O(n^22^n)\) 的,于是 \(f_S=\max\{f_{T}+f_{S-T}\}(T \sub S)\),然后重难点就变成了枚举子集。这里我们使用 T=(T-1)&S
的方法,可以保证 \(T\) 单调递减且是 \(S\) 的子集,于是算上枚举 \(S\) 的复杂度,时间复杂度是 \(O(3^n)\),总的时间复杂度就是 \(O(n^22^n+3^n)\)。
V
设 \(f_u\) 表示包含 \(u\) 的联通块在子树内的情况,\(g_u\) 表示包含 \(u\) 的连通块在子树外的情况。我们可以得到转移:
这里我们不能直接除,因为我们取了模,于是我们选择记前后缀然后相乘。这样 \(O(n)\) 直接过了。
W
首先考虑最朴素的 DP。设 \(f_{i,j}\) 表示前 \(i\) 个数中最后一个 1 在 \(j\) 处。可以得到转移式:
解释:
当 \(i = j\) 是很好说,当 \(i \neq j\) 时,相对于 \(f_{i-1,j}\),能够产生新的贡献的区间,必定满足右端点为 \(i\) ,而左端点大于等于 \(j\),把这些区间的值加上即可
这样暴力转移的话复杂度是 \(O(n^2m)\) ,过不了,考虑优化转移过程。如果把第一位滚掉,只看第二维,我们可以发现第 \(k\) 个区间对 \(f_{l_k}\) 到 \(f_{r_k}\) 都有贡献,那么我们就可以把 f 数组直接扔到线段树上,然后把这个操作变成区间加,复杂度优化至 \(O(\log n)\).这样我们就可以过掉这个题了。
X
暴力就是 01 背包,但优化似乎无从下手,于是我们尝试优化枚举顺序。注意到如果 \(j\) 木块放在 \(i\) 木块上面,则有 \(s_i-w_j > s_n-w_i\),也就是说如果 \(i\) 上面放了 \(j\) 之后还能放的总重量要比反过来多,那肯定要把 \(i\) 放在 \(j\) 下面,然后把刚刚的式子移一下项,得到 \(s_i+w_i>s_j+w_j\),于是我们按 \(s_i+w_i\) 从小到大排序,再去跑 01 背包就会快。
Y
如果我们像 本题的弱化版 那样暴力转移的话,空间和时间都会爆炸,观察到 \(N\) 的取值较小,在可接受范围内,考虑在这 \(N\) 个点上做文章。
先考虑一种特殊情况,当 \(N=0\) 时,也就是没有障碍的时候,设 \(dp_{i,j}\) 表示从 \((1,1)\) 到 \((i,j)\) 有多少种路径。我们可以知道:
其中,\(C_{a}^{b}\) 表示从 \(a\) 个数中选取 \(b\) 个数进行无序组合,也就是组合数。
证明:从 \((1,1)\) 走到 \((n,m)\),必然共走了 \(n+m-2\) 步,其中必然有 \(n-1\) 步向下,选出这 \(n-1\) 步来,剩下的也就是向右走的步数。
当障碍数不为 \(0\) 的时候,就不好用组合数直接统计路径条数了,正着不行反着来,我们考虑算出总共的路径条数,再减去和障碍相关的路径数量,就可以求出答案了。
具体地,我们不妨设点 \((H,W)\) 处为第 \(n+1\) 个障碍,设 \(F_i\) 表示从 \((1,1)\) 到第 \(i\) 个点的合法路径条数。我们找到所有可以到达第 \(i\) 个障碍的障碍 \(j\) ,减去与 \(j\) 相关的路径条数,就可以求出 \(F_i\)。我们可以很容易地列出转移式:
用阶乘和逆元求出组合数,即可通过本题。
Z
是个斜率优化的板子,但是所有斜率优化的题都可以用李超线段树的方式无脑过掉(实在不行就动态开点李超线段树)。
设 \(f_i\) 表示到第 \(i\) 个点的最短距离,可得转移:\( f_i=min\{f_j+(h_j-h_i)^2\}+C \)
暴力拆开,得到 \(f_i=min\{f_j+h_j^2-2h_jh_i+h_i^2\}+C\)
把跟 \(j\) 无关的挪到大括号外面,得到 \(f_i=min\{-2h_j \cdot h_i+f_j+h_j^2\}+h_i^2+C\)
把 \(h_i\) 看做自变量,则 \(-2h_j \cdot h_i+f_j+h_j^2\) 就是个一次函数,可以用李超线段树维护最小值。
(闲话:科技的力量,就是让一个好端端的思维题掉价成板子题啊。)