Re: 从零开始的 dp 学习笔记

你说的对,但是……

前言

太拉了,菜就多练。

题单

在这里就没人看得到我是芙宁娜的狗了

线性 dp

[USACO1.5] [IOI1994]数字三角形 Number Triangles

最经典的题。从中可以领悟出动态规划的本质。

首先,一切 \(dp\) 题都可以用搜索写,复杂度大概是以二为底或者阶乘。这是一个保守的方案,相当于记录全部信息。

而事实上,我们并不需要这么多信息,更多时候只是特殊的那几个,我们称这些需要的信息为后效性

后效性可以是数组下标,也可以是值。

在本题中,我们并不需要前面走的路径的所有信息,只需要了解两个上部分的答案就行了。因此,我们设状态 \(f_{i,j}\) 表示到点 \((i,j)\) 的最大权路径的权和。

然后,我们需要转移,就是把一个问题的答案从另一个问题转移过来。所以需要状态转移方程。本题里状态转移方程为:

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

这道题不太需要实现。

[NOIP1999 提高组] 导弹拦截

\(LIS\) 类问题。如果一个数想要加在一个数后面,只需要比前面那个数大即可。后效性在前面那个数的大小,可以设状态 \(f_{i,j}\) 表示到第 \(i\) 个数时以 \(j\) 结尾的 \(LIS\) 长度。显然每次添加只会改一个 \(j\),所以

\[f_{i,j}=\left\{\begin{matrix} f_{i-1,j} \space (j\ne s_i) \\ \max (f_{i-1,j},\max^{j-1}_{k=1} f_{i-1,k} +1)\space (j=s_i) \end{matrix}\right. \]

这里需要三个优化,不是目前的范围:

  1. 发现 \(i\) 只依赖 \(i-1\),可以把原方程里的 \(i\) 舍弃,称之为滚动数组优化

  2. 发现 \(j\) 的比较只与大小有关,使用可以考虑缩小值域并保持原本顺序,称之为离散化

  3. 发现是求前缀 \(\max\),可以使用线段树或者树状数组等数据结构优化。

复杂度 \(O(n\log n)\)。本题还涉及 \(Dilwith\),在此按下不表。

P3336 [ZJOI2013] 话旧

题意:对于函数 \(f\)\(f_0=f_n=0\),图像中若下降则一定下降到 \(0\),给你 \(k\) 个点,求方案数和可能的最大 \(f_i\)

先考虑后效性,首先要记得就是到第 \(i\) 个点是上升还是下降。我们称其分别为“上升点”和“下降点”。

接下来分几种情况讨论:

空接情况

即两点连接不会下降到 \(0\)

这里又分三种情况:

  • 直线上升,判断是否可以拼接,可以的话两个点都符合上升条件即可。(\(0\) 点特判)

  • 直线下降,同理判断是否可拼接,然后统计答案就行。

  • 先升后降,因为保证有解,这里就不判断是否可拼接了,对应状态即可。(\(0\) 点特判)

注意到空接情况连接方式唯一,不会多算。

地接情况

即两点连接下降到 \(0\)

容易发现下降点下降轨迹固定,上升点上升轨迹固定,最后会在中间留出至少 \(len=x_2-x_1-y_1-y_2\) 的自由区域,里面只要符合条件可以随便放。当然运行接地的前提条件就是 \(len\ge 0\)

第一种情形就是前面的点下降,后面的点上升,这时直接抵满 \(len\) 计算方案数即可。这里的方案数可以考虑计数 \(0\) 点,因为容易发现一个 \(0\) 点只有翻上去和抵下来两种情况。总共 \(\frac{len}{2}-1\)\(0\) 点,所以方案数 \(2^{\frac{len}{2}-1}\)

第二种是两个点都上升,看起来方案数多了不少,很难算。其实可以把第一个上升点落地的 \(0\) 点算上,方案数就显而易见了,多一个 \(0\) 点即 \(2^{\frac{len}{2}}\)。这里如果把 \(1\) 算作上升点会算重,故开始时把 \(1\) 算作下降点。

剩下两种是后一个点下降的情况,发现虽然 \(len\) 长了,但最后的 \(0\) 点少了,所以情况没变。只是注意特判 \(0\) 点。

注意这里有一个地方会算重,当 \(len>0\) 时全部 \(0\) 点往上翻就是空接情况,所以注意不要重复算。

于是解决了方案数,而对于最大值,显然就是尽量往上升。稍微解一下方程得最值 \(\frac{x_2-x_1+y_1+y_2}{2}\)

That's all.

P3558 [POI2013] BAJ-Bytecomputer

题意:对于数组 \(a\) 有 $a_i \in -1,0,1 $,每次操作 \(a_i \longleftarrow a_i+a_{i-1}\),求最小操作数使得数组单调不降,有判无解。

作为一道 dp,这是十分不好想的。

模拟一遍,发现既然要改就不会改的比 \(-1\) 小或者比 \(1\) 大。这是一个关键点,启示我们值域不变。

再想后效性。不降序列的后效性十分明显,就是所有操作完最后的数。

自然设出状态:\(f_{i,j}\) 表示前 \(i\) 个数末尾是 \(j-1\) 的方案数。

转移分讨即可。

P4158 [SCOI2009] 粉刷匠

题意:\(n \times m\) 的格子,粉刷 \(T\) 次横着连续段,重复或不粉刷算错误,求最多能正确几个。颜色只有两种。

又一道神题。

一步步来。dp 的一个重要思想就是使问题互不干扰。本题不会竖着涂,故横条间的后效性独立,这启示我们分别 dp。

先考虑横条。考虑后效性,如果我们涂一个区间,为了使贡献不重,应该拆左右。使用扫描线,就不用管后面了。状态就显然了,\(f_{i,j,k}\) 表示第 \(i\) 个横条,刷了 \(j\) 次,前 \(k\) 个格子的最多正确数。

剩下的都很显然了。

P5301 [GXOI/GZOI2019] 宝牌一大堆

开坑,不写。

背包 dp

P1048 [NOIP2005 普及组] 采药

01 背包板题。

考虑背包问题的本质,就是分配空间维度。

\(W_i(V)\) 为分配给第 \(i\) 个物品 \(V\) 空间所能获得的价值。分配给每物品,再合并即可。

参考树形 dp 二叉合并,显然背包问题是可合并的,将问题转换成分配给前 \(i-1\) 个和分配给第 \(i\) 个。

所以一般背包 dp 的思路就是:递推,枚举前 \(i\) 个物品用的空间,枚举前 \(i-1\) 个物品用的空间,复杂度 \(O(nm^2)\)。注意,背包有单调性,空间越大装的一定越多。

接着考虑 01 背包。01 背包有性质 \(W_i(V)=[V\ge v_i]\times w_i\)。由背包单调性可知,空间要么不给,要么给刚好 \(v_i\) 最优,因此省去枚举 \(i-1\) 的维度,复杂度 \(O(nm)\)

01 背包的滚动数组优化,发现倒序枚举即可避免重复。

另,01 值域背包的转移形式十分适合 bitset 辅助转移。

P1757 通天之分组背包

分组背包。如果理解了背包的本质就比较容易了。先对每一组做一遍维护对应 \(w\),然后再合并即可。

P5289 [十二省联考 2019] 皮配

后记

posted @ 2025-02-11 10:18  一念行空  阅读(13)  评论(0)    收藏  举报