基础 dp 二十六题

题有点多,例行的简要题意环节就无了。这些题都是各个类型的 \(\rm dp\) 里最基础的,就当做再过一遍知识点吧。

A - Frog 1

最基础的一维 \(\rm dp\)。考虑设 \(f_i\) 表示走到 \(i\) 所需要的最少代价,则走到 \(i\) 可以由 \(i-1,i-2\) 转移而来:

\[f_{i}=\min(f_{i-1}+|h_i-h_{i-1}|,f_{i-2}+|h_i-h_{i-2}|) \]

边界条件是走到 \(1\) 不需要代价:

\[f_1=0 \]

答案即为 \(f_n\)。时间复杂度 \(\mathcal{O}(n)\)评测记录

B - Frog 2

是上题的扩展,\(i\) 可以从 \(i-k,i-k+1,\cdots,i-1\) 转移过来:

\[f_i=\min_{j=\max(1,i-k)}^{i-1}\{f_j+|h_i-h_j|\} \]

边界条件和答案不变,时间复杂度 \(\mathcal{O}(nk)\)评测记录

C - Vacation

状态设计变得复杂了一点。注意到每次选择具有后效性,即会影响后一天的选择范围。但对于这种后效性,我们可以通过把选择的内容加入状态来避免。具体来讲,考虑设 \(f_{i,0/1/2}\) 分别表示第 \(i\) 天选活动 A/B/C 的最大幸福度。则有转移:

\[f_{i,j}=\max_{k\in\{0,1,2\}\backslash\{j\}}\{f_{i-1,k}\}+A_{i,j} \]

其中 \(A_{i,0}=a_i,A_{i,1}=b_i,A_{i,2}=c_i\)。边界条件是 \(f_{0,0/1/2}=0\),答案为:

\[\max(f_{n,0},f_{n,1},f_{n,2}) \]

时间复杂度 \(\mathcal{O}(n)\)评测记录

D - Knapsack 1

01 背包问题,不详细讲推导过程。考虑设 \(f_{i,j}\) 表示前 \(i\) 个物品,选出重量为 \(j\) 的最大价值,则有:

\[f_{i,j}=\max(f_{i-1,j},f_{i-1,j-w_i}+v_i) \]

注意重复转移的问题,可以通过倒序枚举 \(j\) 来避免。答案即为 \(\max\{f_{n,0\sim W}\}\)。注意到空间复杂度可以用滚动数组优化为 \(\mathcal{O}(W)\)。进一步观察发现,可以干脆去掉第一维也不影响答案。时间复杂度 \(\mathcal{O}(nW)\)评测记录

E - Knapsack 2

注意到此题和上一题题面都差不多,只不过我们不再能在状态里记录重量了,因为它达到了 \(10^9\) 级别。但同时,价值缩小到了一个可以接受的级别。所以考虑魔改上述过程,设 \(f_{i,j}\) 表示前 \(i\) 个物品,价值为 \(j\) 对应的最小重量:

\[f_{i,j}=\min(f_{i-1,j},f_{i-1,j-v_i}+w_i) \]

注意初始值为 \(f_{0,1\sim W}=\infty,f_{0,0}=0\),依然需要倒序枚举来防止重复转移。类似地,可以把第一维优化掉。最终答案即为最大的 \(i\),满足 \(f_i\le W\)。时间复杂度 \(\mathcal{O}(nV)\)评测记录

F - LCS

经典 LCS 问题,不再介绍推导过程。设 \(f_{i,j}\) 表示 \(S\)\(i\) 个字符与 \(T\)\(j\) 个字符匹配,能得到的最大 LCS,则有转移:

\[f_{i,j}=\max([S_i=T_j](f_{i-1,j-1}+1),\max(f_{i-1,j},f_{i,j-1})) \]

意义就是考虑当前能否匹配上,如果能匹配就尝试匹配,否则就往前考虑。答案即为 \(f_{|S|,|T|}\)。但这道题还要求输出方案,\(\rm dp\) 输出方案通常我们都是记录转移的过程。不过这道题稍微有点麻烦,因为还涉及到哪个转移匹配上,哪些不匹配。考虑设 \(las_{i,j}\) 表示 \((i,j)\) 是从哪个二元组转移过来的,设 \(v_{i,j}\) 表示 \((i,j)\) 对应的匹配,如果没有就置为空。转移的时候记录一下,最终输出时从 \((|S|,|T|)\) 往前倒推整个过程即可。时间复杂度 \(\mathcal{O}(|S||T|)\)评测记录

G - Longest Path

DAG 上 \(\rm dp\)。考虑拓扑排序。如果存在一条 \(u\rightarrow v\) 的边,则有转移:

\[f_v=\max(f_u+1,f_v) \]

最终答案即为:

\[\max_{i=1}^n\{f_i\} \]

评测记录

H - Grid 1

经典网格上 \(\rm dp\)。考虑设 \(f_{i,j}\) 表示从 \((1,1)\)\((i,j)\) 的方案数,初始时 \(f_{1,1}=1\),答案即为 \(f_{H,W}\)。转移时,考虑从上还是左转移:

\[f_{i,j}=f_{i-1,j}+f_{i,j-1} \]

如果遇到障碍不转移即可。时间复杂度 \(\mathcal{O}(HW)\)评测记录

注意到,如果我们再观察一下 \(\rm dp\) 的转移:

\[f_{i,j}=\dbinom{i+j}{i}=\dbinom{i+j-1}{i-1}+\dbinom{i+j-1}{i}=f_{i-1,j}+f_{i,j-1} \]

可以发现,如果不存在障碍,能证明:

\[f_{i,j}=\dbinom{i+j}{i} \]

组合意义也比较好看出来,发现一共要走 \(i+j\) 步,我们只需要选出 \(i\) 步朝上的即可。

I - Coins

概率与期望 \(\rm dp\)。有一个比较显然的三维状态,考虑设 \(f_{i,j,k}\) 表示前 \(i\) 个有 \(j\) 个正面朝上的,\(k\) 个背面朝上的的概率。最终答案即为 \(\sum\limits_{j=1}^n\sum\limits_{k=1}^{j-1}f_{n,j,k}\)。这样转移的复杂度是 \(\mathcal{O}(n^3)\) 的,无法接受。但注意到,我们关心的是 \(j,k\) 的差,而不是具体值,所以考虑更改状态的设计。设 \(f_{i,j}\) 表示前 \(i\) 个,正面朝上的减去背面朝上的个数为 \(j\) 的概率。则转移为枚举这一位是正还是反:

\[f_{i,j}=f_{i-1,j-1}p_i+f_{i-1,j+1}(1-p_i) \]

答案即为 \(\sum\limits_{j=0}^nf_{n,j}\)。注意这里可能要处理负下标,平移一下即可。时间复杂度 \(\mathcal{O}(n^2)\)评测记录

J - Sushi

概率与期望 \(\rm dp\)。注意到,有用的信息是剩下盘子里寿司的数量,我们需要设计一个合理的状态来描述。考虑设 \(f_{i,j,k}\) 表示剩下 \(1,2,3\) 个寿司的盘子分别有 \(i,j,k\) 个时,需要的操作次数。则转移就比较明显,考虑枚举这次随机到哪种类型的盘子即可:

\[f_{i,j,k}=\dfrac{i}{n}f_{i-1,j,k}+\dfrac{j}{n}f_{i+1,j-1,k}+\dfrac{k}{n}f_{i,j+1,k-1}+\dfrac{n-i-j-k}{n}f_{i,j,k}+1 \]

把等式右边的 \(f_{i,j,k}\) 移到左边即可正常递推。除此之外,注意这个转移顺序比较难找,所以直接上记忆化搜索即可,边界条件为 \(f_{0,0,0}=0\),答案为 \(f_{cnt_1,cnt_2,cnt_3}\)。时间复杂度 \(\mathcal{O}(n^3)\)评测记录

K - Stones

博弈论。注意到,如果我们把游戏抽象为一张 DAG,每个点表一种剩余的石头数量,则我们会得到一张 \(\mathcal{O}(k)\) 个点,\(\mathcal{O}(nk)\) 条边的有向图。考虑一个点是先手必胜的,当且仅当它的出边存在一个点是先手必败的点,反之就是先手必败的。发现可以用记忆化搜索搜索这个图的状态,求出每个点的必胜必败状态,共 \(\mathcal{O}(k)\) 个状态,单次转移 \(\mathcal{O}(n)\),总时间复杂度 \(\mathcal{O}(nk)\)评测记录

L - Deque

还是博弈论。不过这次如果再抽象为 DAG 就点数太多了。考虑 \(\min\max\) 博弈,即我们考虑当前状态下是先手还是后手操作,然后分别根据它们的最优策略转移。具体来讲,设 \(f_{l,r}\) 表示序列剩下的区间为 \([l,r]\) 时的答案,则:

\[\begin{cases}f_{l,r}=\min(f_{l+1,r}-a_l,f_{l,r-1}-a_r)&n-r+l-1\equiv1\pmod{2}\\f_{l,r}=\max(f_{l+1,r}+a_l,f_{l,r-1}+a_r)&\tt otherwise\end{cases} \]

区间 \(\rm dp\) 即可,时间复杂度 \(\mathcal{O}(n^2)\)评测记录

M - Candies

前缀和优化 \(\rm dp\)。考虑设 \(f_{i,j}\) 表示前 \(i\) 个小孩分 \(j\) 个糖的方案数,则最终答案即为 \(f_{n,k}\)。一个比较显然的转移是:

\[f_{i,j}=\sum_{k=0}^{\min(j,a_i)}f_{i-1,j-k} \]

时间复杂度 \(\mathcal{O}(nk^2)\),无法通过。注意到这个转移是区间和的形式,可以直接上前缀和优化,时间复杂度降至 \(\mathcal{O}(nk)\)评测记录

N - Slimes

区间 \(\rm dp\)。考虑设 \(f_{l,r}\) 表示合并区间 \([l,r]\) 内的史莱姆需要的最小代价,则枚举当前区间是由哪两个子区间合并而来的可以得到:

\[f_{l,r}=\min_{k=l}^{r-1}(f_{l,k}+f_{k+1,r}+\operatorname{sum}_{l,r}) \]

其中 \(\operatorname{sum}_{l,r}\) 表示区间和,可以通过维护前缀和 \(\mathcal{O}(1)\) 得到。时间复杂度 \(\mathcal{O}(n^3)\)评测记录

O - Matching

状压 \(\rm dp\)。考虑设 \(f_S\) 表示当前男性的匹配情况为 \(S\) 时的方案数。这里需要解释的是,我们是按照顺序匹配女性的,即将一个男性加入集合时,我们钦定他匹配的是第 \(|S|+1\) 个女性。这样,\(f_S\) 的意义其实就是对于当前集合,有多少种合法的加入顺序。转移只需枚举上一次加入的是哪个男性:

\[f_S=\sum_{v\in S,a_{v,|S|-1}=1}f_{S\backslash\{v\}} \]

初始时有 \(f_{\{0,0,0\cdots\}}=1\),答案即为 \(f_{\{1,1,1,\cdots\}}\)。时间复杂度 \(\mathcal{O}(2^nn)\)评测记录

P - Independent Set

树形 \(\rm dp\)。考虑设 \(f_{i,0/1}\) 表示结点 \(i\) 为白/黑色的方案数,则转移时,我们考虑将儿子的方案合并上来:

\[\begin{aligned}&f_{i,0}=\prod_{v\in son_i}(f_{v,0}+f_{v,1})\\&f_{i,1}=\prod_{v\in son_i}f_{v,0}\end{aligned} \]

一遍 \(\rm dfs\) 即可转移,最终答案即为 \(f_{rt,0}+f_{rt,1}\),其中 \(rt\)\(\rm dfs\) 时钦定的根。时间复杂度 \(\mathcal{O}(n)\)评测记录

Q - Flowers

带权的 LIS。比较经典的问题,考虑设 \(f_i\) 表示前 \(i\) 朵花能得到的最大价值,则转移时只需要往前面找一朵花接上即可:

\[f_i=\max_{1\le j<i,h_j<h_i}\{f_j\}+a_i \]

直接转移是 \(\mathcal{O}(n^2)\) 的无法接受。注意到关于下标的限制是很弱的,我们只需要处理完 \(f_i\) 后把它扔到某个我们维护的东西里面,之后再查询时就能保证有效区间是合法的。所以我们把重点放在 \(h\) 的限制上。发现 \(h\) 的值域是很小的,考虑开一个 DS,第 \(x\) 位维护满足 \(h_i=x\)\(f_i\) 最大值。则转移时,我们只需查询一个前缀最大值 \([1,h_i-1]\) 即可。可以用树状数组轻松实现上述功能,时间复杂度 \(\mathcal{O}(n\log n)\)评测记录

另外,如果 \(h\) 的值域很大,因为我们只关心大小关系,所以离散化后就能做了。

R - Walk

矩阵乘法优化 \(\rm dp\)。考虑设 \(f_{i,j,k}\) 表示从 \(i\)\(j\),经过恰好 \(k\) 条边的方案数。容易发现 \(f_{i,j,1}\) 即为邻接矩阵。转移时,我们有:

\[f_{i,j,k}=\sum_{l=1}^nf_{i,l,k-1}f_{l,j,k} \]

发现这个很像矩阵乘法的形式,更进一步,如果我们设邻接矩阵为 \(\bf A\),则应该有:

\[f_{i,j,k}=\mathbf{A}^k \]

归纳法易证。所以最终答案即为:

\[\sum_{i=1}^n\sum_{j=1}^n(\mathbf{A}^k)_{i,j} \]

时间复杂度 \(\mathcal{O}(n^3\log k)\)评测记录

S - Digit Sum

数位 \(\rm dp\)。考虑设 \(f_{i,j,0/1}\) 表示考虑了前 \(i\) 位,数位和模 \(d\)\(j\),是否贴着 \(k\) 的上界。则转移有:

\[\begin{aligned}&f_{i,j,0}=\sum_{l=0}^9f_{i+1,(j+l)\bmod d,0}\\&f_{i,j,1}=\sum_{l=0}^{k_i}f_{i+1,(j+l)\bmod d,[l=k_i]}\end{aligned} \]

其中 \(k_i\) 表示 \(k\)\(i\) 位的值。这个过程因为参数比较多,所以用记忆化搜索实现会更简单,时间复杂度 \(\mathcal{O}(d\log k)\)评测记录

T - Permutation

这个好像国内不知名,国外称之为插入 \(\rm dp\),主要思路就是每次转移就是往某个序列中插入一个元素。注意到此次转移只和上一次填入的数有关,再加上排列的限制,我们考虑设 \(f_{i,j}\) 表示前 \(i\) 个数,填入 \(1\sim i\),最后一个数是 \(j\) 的方案数。转移就是考虑下一位填什么,但注意,我们要保证填的数是排列,所以加入 \(j\) 时,我们需要把所有 \(\ge j\) 的数向前平移一位,这大概就是插入这个名字的来源吧。这样,转移就比较好想了:

\[f_{i,j}=\begin{cases}\sum\limits_{k=1}^{j-1}f_{i-1,k}&s_i= \mathtt{<}\\\sum\limits_{k=j}^{i-1}f_{i-1,k}&s_i=\mathtt{>}\end{cases} \]

前缀和优化一下即可做到 \(\mathcal{O}(n^2)\) 的时间复杂度。评测记录

U - Grouping

状压 \(\rm dp\)。容易想到设 \(f_S\) 表示已经考虑了 \(S\) 内的元素能得到的最大价值,则枚举最后一次加入的子集可以得到转移:

\[f_S=\max_{T\subseteq S}\{f_{S\backslash T}+\operatorname{sum}_{T}\} \]

其中 \(\operatorname{sum}_S\) 表示把 \(S\) 集合内所有的元素归为一个集合的贡献。用枚举子集的技巧可以做到 \(\mathcal{O}(3^n)\) 的转移,算上预处理的复杂度,总时间复杂度 \(\mathcal{O}(2^nn^2+3^n)\)评测记录

V - Subtree

二次扫描换根 \(\rm dp\)。注意到,原题相当于问原树的所有连通块中,包含 \(v\) 的有多少个。而这个问题,我们对原树的根是非常好回答的,具体来讲,如果我们设 \(f_u\) 表示只考虑以 \(u\) 为根的子树,包含 \(u\) 的连通块个数,则有:

\[f_u=\prod_{v\in son_u}(f_v+1) \]

即每个子树可以要或者不要,乘起来即为答案。这样,根的答案即为 \(f_{rt}\)。如果我们做 \(n\) 遍树形 \(\rm dp\),可以得到一个 \(\mathcal{O}(n^2)\) 的做法,无法通过。

考虑二次扫描换根 \(\rm dp\),这个做法的主体思路是先随便钦定一个根做一遍树形 \(\rm dp\),然后再 \(\rm dfs\) 一遍树,到达一个结点时,根据当前有的信息求出以它为根的答案。具体到这道题,假设第一遍 \(\rm dp\)\(1\) 为根,则我们考虑再设一个 \(g_u\) 表示非在以 \(1\) 为根时 \(u\) 的子树对 \(u\) 的贡献,最终每个点的答案即为 \(f_ug_u\)

转移时,我们不妨设 \(g_{fa_u}\) 已经求出,则对于 \(g_u\),它比 \(g_{fa_u}\) 多的贡献是,\(fa_u\)\(1\) 为根时的所有除了 \(u\) 的子树的 \(f\)。具体来讲,有:

\[g_u=\dfrac{g_{fa_u}f_{fa_u}}{f_u+1}+1 \]

\(+1\) 是因为上面也可以不选。

但非常不幸,这道题不保证逆元存在,所以除法寄了。不过没问题,注意到:

\[\dfrac{f_{fa_u}}{f_u+1}=\prod_{v\in son_{fa_u},v\ne u}(f_v+1) \]

维护个前缀和后缀积即可。时间复杂度 \(\mathcal{O}(n)\)

W - Intervals

线段树优化 \(\rm dp\)。发现区间的值域都不大(大了也可以离散化),所以我们一格一格考虑是否放入 \(1\) 和区间的贡献。

考虑我们考虑的点向右推进的过程,当我们碰到一个区间的右端点时,这个区间是否做贡献,取决于上一个放的 \(1\) 是否在它左端点的右边。所以我们先把区间按照右端点单调不降排序,然后设 \(f_{i,j}\) 表示考虑到第 \(i\) 个点,上一个 \(1\)\(j\) 的最大分数。转移就比较好想了:

\[\begin{cases}f_{i,j}=f_{i-1,j}+\sum\limits_{r_p=i,l_p\le j}a_p&j<i\\f_{i,j}=\max\limits_{k<i}\{f_{i-1,k}\}+\sum\limits_{r_p=i,l_p\le j}a_p&j=i\end{cases} \]

直接转移是 \(\mathcal{O}(n^2)\) 的无法通过。考虑优化,注意到转移的两个部分,前一个部分是区间取 \(\max\),后一个部分是区间加。(如果我们把条件看为 \(j\ge l_p\) 的话)这都是线段树能干的事。

具体来讲,考虑把 \(\rm dp\) 数组扔到线段树上,初始时第 \(j\) 个位置维护 \(f_{0,j}=-\infty\)。接着每次转移,集体将第 \(j\) 个位置的 \(f_{i,j}\) 更新为 \(f_{i+1,j}\)。我们先不考虑后面的和式,则需要更新的只有 \(f_{i,i}\),一次区间 \(\max\) 即可。然后对于后面的和式,我们只需要给 \(\ge l_p\) 的所有 \(j\) 加上 \(a_p\) 即可,一次区间加即可。最终答案即为 \(\max\{f_{n,1\sim n}\}\)。时间复杂度 \(\mathcal{O}(n\log n)\)评测记录

X - Tower

性质优化 \(\rm dp\)。关键问题在于怎么合理分配放置的顺序,注意到一个物品,可以转移到的重量区间为 \([w_i,w_i+s_i]\),则我们尽量想让后转移的区间比前转移的区间靠后,这样能尽量继承前面的,得到更大的价值。所以,我们考虑按照 \(w_i+s_i\) 单调不降的顺序排序。之后,做 01 背包即可。时间复杂度 \(\mathcal{O}(n\log n+nS)\)评测记录

Y - Grid 2

状态设计优化 \(\rm dp\)。发现网格很大,没法直接 \(\rm dp\),但注意到障碍点的个数很小,所以可以考虑从障碍点入手。

考虑设 \(f_i\) 表示从 \((1,1)\) 走到第 \(i\) 个障碍点,且不经过任何其他障碍点的方案数,这样,如果我们把 \((H,W)\) 设为第 \(n+1\) 个关键点,答案即为 \(f_{n+1}\)。转移时,我们先考虑不加任何限制的情况,然后减去路上经过关键点的情况,即:

\[f_{i}=\dbinom{x_i-1+y_i-1}{x_i-1}-\sum_{j=1}^n[x_j\le x_i,y_j\le y_i]f_{j}\dbinom{x_i-x_j+y_i-y_j}{x_i-x_j} \]

组合数的意义可以参考 Grid 1 的组合意义。预处理出阶乘和逆元,即可做到 \(\mathcal{O}(n^2)\) 的时间复杂度。评测记录

Z - Frog 3

斜率优化 \(\rm dp\)。延续 Frog 系列题的套路,设 \(f_i\) 表示走到 \(i\) 需要的最小代价,则有转移:

\[f_i=\min_{j=1}^{i-1}\{f_j+(h_j-h_i)^2+C\} \]

简单推一推式子,把与 \(j\) 无关的扔到左边:

\[f_i-h_i^2-C=\min_{j=1}^{i-1}\{f_j+h_j^2-2h_ih_j\} \]

考虑直线 \(y=kx+b\),变一变即为 \(b=y-kx\),发现上面的式子好像很符合。发现,\(y,x\) 都是只关于 \(j\) 的(\(y_j=f_j+h_j^2,x_j=h_j\)),而 \(k\) 是关于 \(i\) 的(\(k=h_i\)),所以本质上相当于平面上有一些点,我们要拿一条已知斜率的直线去切这个点,满足得到的 \(y\) 轴截距最小。

注意到,这样的点一定在下凸壳上,所以考虑对 \((x_j,y_j)\) 维护下凸壳。注意到每次切的斜率是单调递增的,所以每次能切到的点是能单调向前运动的,即如果我们维护的单调队列的队头和下一个点的斜率小于 \(2h_i\),则这个点就永远不可能成为决策点,弹出即可。之后队头就为最优决策点,之后再把当前点对应的决策点加入下凸壳的维护。时间复杂度 \(\mathcal{O}(n)\)评测记录

posted @ 2022-04-12 18:19  zhiyangfan  阅读(196)  评论(0编辑  收藏  举报