0x50动态规划 做题记录
你不会的题,很可能就是 DP
先决条件:问题与状态空间、递归与递推、搜索
0x50 总论
碎碎念:常常,一个大的问题是很难解决的,考虑类似于数学归纳法,通过问题的可划分性以及子问题之间的相似性来进行归纳,使它更易于解决;动态规划也不例外。通过将问题划分为几个 重叠的子问题,这些求解子问题的过程就是 阶段,并通过这些子问题的解得出原问题的解,正是动态规划降复杂度的奥秘所在。
这一性质就被称作问题的 最优子结构,实际上,我们对于每个子问题同时也只保留这些信息,作为当前子问题的 状态。这就要求这些代表信息应该具有可重复的求解过程,并能够导出后续阶段的代表信息。同时,为了使问题的求解有序,我们通常采用固定的顺序,将问题在某些维度上划分,将阶段在这些维度上的增长看成整个问题求解的顺序,而具体地,如何将小问题扩展为大问题则是 决策,动态规划每一步的转移都对应着一步决策,且最好需要保证问题中我们当前的决策选择不会对后面的选择造成影响,也就是 无后效性。
“阶段”、“状态”和“决策”是构成动态规划的三要素;而“重叠子问题”、“最优子结构”和“无后效性”是问题能用动态规划求解的三个基本条件。可以看出,这六点两两之间是有内在联系且一一对应的。
0x51 线性 DP
DP 的转移沿着各个维度线性增长。
AcWing 271. Mr.Young's Picture Permutations
有 \(N\) 个学生合影,站成左端对齐的 \(k\) 排,每排分别有 \(N_1, N_2, \ldots, N_k\) 个人,\((N_1 \ge N_2 \ge \ldots \ge N_k)\),第 \(1\) 排站在最后边,第 \(k\) 排站在最前边。学生的身高互不相同,把他们从高到底依次标记为 \(1, 2, \ldots, N\)。在合影时要求每一排从左到右身高递减,每一列从后到前身高也递减。问一共有多少种安排合影位置的方案?\(N \le 30, k \le 5\)。
我们直接从高到低考虑每个人都位置,这样就能直接满足方案中单调的要求。在本题中,要理解的一点便是:轮廓也可以作为状态。当题目中要求的高低次序已经满足,我们只需要考虑把人放在这的这个位置合不合法即可。这样,我们状态的表示就与之前已经站好的人无关,而一个 \(k\) 元组 \((a_1, a_2, a_3, \ldots, a_k)\) 描绘的轮廓内的合影方案总数就足以构成一个子问题,从而在人数上转移。
AcWing 272. LCIS
对于两个数列 \(A\) 和 \(B\),如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,求所有的公共上升子序列中最长的公共上升子序列的长度。\(|A|, |B| \le 3000\)
LCS 问题需要知道当前考虑到了 \(A, B\) 串的哪个位置,LIS 问题需要知道当前串的结尾是多少。而在实际做的时候,你一定不想 \(B\) 串同时考虑求解的位置与结尾这两个状态,所以我们可以在 LIS 的基础上扩展,在已上升的子序列中,考虑公共的部分,涉及状态时将 \(A\) 套到外层即可:\(F[i, j]\) 表示 \(A_1 \sim A_i\) 与 \(B_1 \sim B_j\) 可以构成的以 \(B_j\) 结尾的 LCIS 的长度(\(B\) 序列 LIS,同时与 \(A\) 序列 LCS)。
转移时,有:
朴素方法使用三重循环解决即可。但在转移过程中,我们关注决策集合,(我们把满足 \(0 \le k < j, B_k < B_j\) 的 \(k\) 构成的集合称为 \(F[i, j]\) 进行状态转移时的决策集合,记为 \(S(i, j)\)。注意到,\(j\) 从 \(1\) 变为 \(m\) 的过程中,整数 \(j\) 可能进入决策集合仅当 \(B_j < A_i\),而已经在决策集合中的数一定不会删除,对于“决策集合中的元素只增多不减少”的情景,我们用一个数据结构来维护这个最大值,即可避免重复扫描,把转移的复杂度降低一个量级。
AcWing 273. Making the Grade
给定长度为 \(N\) 的序列 \(A\),构造一个长度为 \(N\) 的序列 \(B\),满足:
- \(B\) 非严格单调,即 \(B_1 \le B_2 \le \ldots \le B_N\) 或 \(B_1 \ge B_2 \ge \ldots \ge B_N\)。
- 最小化 \(S = \sum^N_{i = 1}|A_i − B_i|\)。
求出这个最小值 \(S\)。
考虑 \(N^2\) 做法,其瓶颈在于题目并不允许我们记录当前结尾选了什么数,而 LIS 问题之所以不用考虑这个问题就是在于“子序列”这个东西表明选的数一定在 \(A\) 现过,而我们要求的是代价最小,可以贪心而大胆地想一下构造时我们所用到的数都是在 \(A\) 过的。
用贪心的性质来简化动归的设计
我们按阶段的划分进行数学归纳,就是一个货仓选址。于是有 \(f[i][j]\) 表示完成前 \(i\) 个的构造,\(B_i = j\) 的最小值。
\(j\) 在扩展时,显然只会多出来一个点,所以可以变量维护一下 \(O(1)\) 转移
AcWing 274. Mobile Service
一个公司有三个移动服务员,最初分别在位置 \(1, 2, 3\) 处。
如果某个位置(用一个整数表示)有一个请求,那么公司必须指派某名员工赶到那个地方去。某一时刻只有一个员工能移动,且不允许在同样的位置出现两个员工。从 \(p\) 到 \(q\) 移动一个员工,需要花费 \(c(p, q)\)。这个函数不一定对称,但保证 \(c(p, p) = 0\)。
给出 \(N\) 个请求,请求发生的位置分别为 \(p_1 \sim p_N\)。公司必须按顺序依次满足所有请求,且过程中不能去其他额外的位置,目标是最小化公司花费,请你帮忙计算这个最小花费。\(N \le 1000\),位置是 \(1 \sim 2000\) 的整数。
这题的状态和转移非常好想:记录三个服务员的位置 \((x, y, z)\),然后分阶段分别让这三个服务员去服务第 \(i + 1\) 个请求,代价取最小值。问题是:这个状态储存和转移的量非常大,细想之后可以发现:服务完第 \(q_i\) 个请求后,一定有且仅有一个服务员在第 \(q_i\) 个请求的位置,所以我们记录三个人其实是多了,故我们只需记录两个人的位置,剩下那一个人可以推导得出,就像线性代数的线无关组,减少了多余的状态和转移。
两点启发:
- 求解线性 DP 问题,一般先确定“阶段”。若“阶段”不足以表示一个状态,则可以把所需的附加信息也作为状态的维度。无后效性由“阶段”保证。
- 确定 DP 状态时,要选择最小的能够覆盖整个状态空间的“维度集合”。
0x52 背包 DP
AcWing 281. 硬币
给定 \(N\) 种硬币,其中第 \(i\) 种硬币的面值为 \(A_i\),共有 \(C_i\) 个。从中选出若干个硬币,把面值相加,若结果为 \(S\),则称“面值 \(S\) 能被拼成”。求 \(1 \sim M\) 之间能被拼成的面值有多少个。
\(1 \le N \le 100\),\(1 \le M \le 10^5\),\(1 \le A_i \le 10^5\),\(1 \le C_i \le 10^3\)
题目描述十分简单,可以直接套多重背包优化;不过,题目中没有价值这个维度,只考虑可行性,所以我们实际上可以设计一种贪心策略。可以发现,如果一个面值能拼出来,有两种可能:既可能不用 \(i\) 就能拼出来,也可能需要在之前的基础上使用几个 \(i\) 才能拼出来。我们就考虑贪心地让面值 \(j\) 使用的硬币数尽量少。也就是说,\(f_j\) 与 \(f_{j - a_i}\) 都为 \(true\) 时,不转移,并令 \(use_j \gets 0\)。否则才由之前的面值得到,并更新 \(use_j \gets use_{j - a_i} + 1\)。实现时采用完全背包的循环模型,用 \(use\) 控制次数,同时 \(f_j\) 为 \(false\) 时进行转移,保证不会漏掉可行解。代码。
0x53 区间 DP
P2400 秘密文件
三倍经验:P4302(弱弱化),UVA1630(弱化)
若一个字符串 \(s\) 连续出现 \(k\) 次,则可以压缩为 \(k(s)\)。如 \(\texttt{ABABAB}\) 可以缩成 \(\texttt{3(AB)}\)。允许嵌套,如 \(\texttt{ABABABAAAAAAABABABAAAAAA}\) 可以压缩为 \(\texttt{2(3(AB)6(A))}\)。求对于给定的字符串,最短的压缩方案。
注意:有多个最优方案时,输出其中字典序最大的。
很明显区间 DP,设 \(f_{l, r}\) 表示 \(l \sim r\) 折叠成的最小长度。考虑如何转移,一段区间既可能由更小的区间横向拼接得到,也可能存在循环节,进行折叠得到。
-
转移 1:拆成两部分
\(f_{l, r} = \min\limits_{l \le k < r}\{f_{l, k} + f_{k + 1, r}\}\) 的转移不再多说。 -
转移 2:循环生成
接下来如果按照一般思路,考虑 \(l \sim r\) 有哪个子串循环生成,会稍微有点麻烦。
我们知道,动态规划的实现有两个方向:既可以考虑 \(f_{l, r}\) 如何得到(填表法),也可以考虑 \(f_{l, r}\) 能转移到哪些新的状态(刷表法);甚至可以把两个方向结合起来——只要按照合理的阶段顺序把所有状态都计算一遍就行了。为了统一这两种操作且同时不增加复杂度,我们试试“双向”转移,结合这两种转移方法。所以对于转移 2,我们可以改成不断重复 \(f_{l, r}\),尝试用它更新更长的 \(f_{l, rr}\),其中 $ rr = r + len, r + 2 \times len, \dots$
题目还要求打印方案,这题里是比较套路的操作,记录转移路径,递归输出即可。代码。
0x5A 斜率优化
处理 \(val(i, j)\) 中有关于 \(i, j\) 乘积时的情况,比较好想好写,问题越单调越好写
P2900 [USACO08MAR] Land Acquisition G
对于 \(N\) 块长方形的土地,第 \(i\) 块土地的长和宽为 \(x_i\) 和 \(y_i\),如果单买一块土地,价格就是土地的面积;并购一组土地,并购的价格为这些土地中最大的长乘以最大的宽。给定每份土地的尺寸,求购买所有土地所需的最小费用。
\(1 \leq N \leq 5 \times 10^4\),\(x_i, y_i \leq 10^6\)
对于两块土地 \(a\) 和 \(b\),如果有 \(x_a \ge x_b\) 且 \(y_a \ge y_b\),那么我们并购这两块土地一定是最优的,其中较小的那块土地没有任何贡献。这样,我们对输入的 \(N\) 块土地进行预处理,得到 \(x\) 值非严上升,\(y\) 值严格下降的新序列。之后 DP 对子序列进行划分就行了。设 \(f_i\) 表示购买前 \(i\) 块土地的最小代价,有转移:
方便起见,我们可以把 \(y\) 数组统一向右移动一单位,整理移项得到:
现在就体现出了我们当时按 \(x\) 坐标升序,\(y\) 坐标降序排列的好处,现在 \(K = -y_j, X = x_i\) 且均单调,当然作为练习建上凸包也可以。代码。
P3628 [APIO2010] 特别行动队
则有直线 \(K_iX_j + B_i = Y_j\),其中 \(K_i\) 单减,\(X_j\) 单增
P3195 [HNOI2008] 玩具装箱
则有直线 \(K_iX_j + B_i = Y_j\),其中 \(K_i, X_j\) 单增
P4027 [NOI2007] 货币兑换
对于金券交易所的两种金券:A 和 B 券,金券的数目可以是一个实数;每天两种金券都有一定的价值。我们记录第 \(K\) 天中 A 券和 B 券的价值分别为 \(A_K\) 和 \(B_K\)(元/单位金券)。有两种操作:
- 卖出金券:顾客提供一个 \([0, 100]\) 内的实数 \(OP\) 作为卖出比例,其意义为:将 \(OP\%\) 的 A 券和 \(OP\%\) 的 B 券以当时的价值兑换为人民币;
- 买入金券:顾客支付 \(IP\) 元人民币,交易所将会兑换给用户总价值为 \(IP\) 的金券,并且,满足提供给顾客的 A 券和 B 券的比例在第 \(K\) 天恰好为 \(\mathrm{Rate}_ K\);
同一天内可以进行多次操作。已知未来 \(N\) 天内的 A 券和 B 券的价值以及 \(\mathrm{Rate}\)。求如果开始时拥有 \(S\) 元钱,那么 \(N\) 天后最多能够获得多少元钱。
\(N \le 10^5\),\(0 < A_K, B_K\le 10\),\(0 < \mathrm{Rate}_K \le 100\),\(\mathrm{Ans} \leq 10^9\)。
必然存在一种最优的买卖方案满足:每次买进操作使用完所有的人民币,每次卖出操作卖出所有的金券。
题面已经给出的本题的性质:对于所有操作,最大化这次买入 / 卖出的数量,这使得我们可以用 DP 来做这道题;然而在一般情况下题目给出的信息都很复杂,读题找出题目的贪心性质是得出 DP 解法重要与不可或缺的一环。设 \(f_i\) 为第 \(i\) 天最多拥有的钱数,\(x_i\) 为第 \(i\) 天用 \(f_i\) 元钱可以兑换的 A 券数,\(y_i\) 为 B 券数。
则有
而第 \(i\) 天有不卖出和卖出金券两种选择,即
我们先管卖出金券的情况,套用前面式子的形式,变成
则 \(K = -\dfrac{A_i}{B_i}, X = x_j\),平面上有众多的点 \((x_j, y_j)\),直线切得最大截距的点即为最优决策,维护上凸包即可;其中,\(K\) 与 \(X\) 都不单调。在这里我使用了 CDQ 分治维护。
我们手动用一层 CDQ 来维护单调性。外层为时间;内层中:
- 对于左半边维护 \(x_i\) 有序,建出一个斜率单调递减的凸包,单调栈维护;
- 右半边维护 \(k_i =-\dfrac{A_i}{B_i}\) 有序。这时左右两半区间就可以进行 \(O(n)\) 的更新。
代码。
0x5B 决策单调性优化
四边形不等式:设 \(w(x, y)\) 是定义在整数集合上的二元函数。若对于定义域上的任意整数 \(a, b, c, d\),其中 \(a \le b \le c \le d\),都有 \(w(a, d) + w(b, c) \ge w(a, c) + w(b, d)\) 成立,则称函数 \(w\) 满足 四边形不等式。
推论:前提不变,若对于定义域上的任意整数 \(a, b\),其中 \(a < b\),都有 \(w(a, b + 1) + w(a + 1, b) \ge w(a, b) + w(a + 1, b + 1)\) 成立,则函数 \(w\) 满足 四边形不等式。
P4767 [IOI2000] 邮局
整数轴上有 \(V\) 座村庄,位置用单个整数坐标标识。没有两个在同样地方的村庄。邮局将建在其中 \(P\) 座村庄中,且使每个村庄与其最近的邮局之间的距离总和最小。已知村庄的位置和邮局的数量,计算每个村庄和最近的邮局之间所有距离的最小可能的总和。
\(1 \leq P \leq 300\),\(P \leq V \leq 3000\)
不是很理解题解区为啥都用二维决策单调性。
第一性质就是答案一定是一个邮局对应一段区间上的村庄,第二性质就是一段村庄的邮局设在他的中位数处最小。然后就可以 DP 了:设 \(f_{i, j}\) 表示在前 \(j\) 座村庄设了 \(i\) 座邮局时的最小距离总和,有转移:
代码。
0x5C 计数 DP
P5694 [NOI2001] 陨石的秘密
给定 \(L1\) 个 \(\texttt{{}}\),\(L2\) 个 \(\texttt{[]}\),\(L3\) 个 \(\texttt{()}\),让你使用它们组成一些括号序列,规定:
- 在纵向上,大、中、小括号只能依次嵌套,如 \(\texttt{{[()]}}\);
- 在横向上,小的括号序列能任意拼接成更大的括号序列,如 \(\texttt{[](){}}\);
- 一个括号序列的深度定义为纵向嵌套中嵌套的最大层数。
问合法的深度为 \(D\) 的括号序列有多少种?
根据 \(\texttt{{}}\)、\(\texttt{[]}\)、\(\texttt{()}\) 和 \(\texttt{+}\)(连接)四种操作把原问题划分成规模更小的子问题,同时为了避免重复计数,转移时划分为构成“第一段”的部分和剩下的部分作为子问题,如 \(\texttt{[S]{[SS](SSS)}}\),就选取 \(\texttt{[S]}\) 作为它的第一段序列。设 \(f_{p, i, j, k}\) 表示深度不超过 \(p\) 时,分别使用了 \(i, j, k\) 个不同括号时的方案数,在转移时我们考虑枚举第一段的外层括号以及第一段的深度、两端各自的括号数即可。
现在 DP 状态的转移即为:
- 考虑第一段的最外层括号
- 枚举第一段的括号构成
初值:\(f_{i, 0, 0, 0} = 1\),答案:\(f_{D, L1, L2, L3} - f_{D - 1, L1, L2, L3}\),复杂度:\(O(DL^6)\)。代码。
0x5E DP 凸优化
优化 DP 才是带权二分最好的归宿。
P5896 [IOI2016] aliens(异形)
一个 \(m \times m\) 的网格上有 \(n\) 个关键点,要求选至多 \(k\) 个两顶点在主对角线上的正方形去覆盖它们。问这些正方形最少要覆盖多少个方格。
\(1 \le k \le n \le 10^5\),\(1 \le m \le 10^6\)。
由于每个正方形的两顶点都必须在主对角线上,所以如果想覆盖到一个关键点,也就等价于覆盖主对角线上 \([\min\{x_i, y_i\}, \max\{x_i, y_i\}]\) 的区间,这样就把原问题转化成了一个区间覆盖问题。把所有关键点都翻折到右上角并排序后,考虑如下两条线段 \(a, b\),如果 \(l_a \le l_b \wedge r_a \ge r_b\),那么 \(b\) 线段就是不产生贡献的,可以把这些线段删去,最后得到 \(l_{i - 1} < l_i\),\(r_{i - 1} < r_i\) 的线段序列。我们先不考虑正方形个数的限制,这时候就是一个很套路的划分子段,设 \(f_i\) 表示我们覆盖了前 \(i\) 条线段,总共覆盖的最少方格数,有转移:
看起来没有什么问题,对吧;可以用斜率优化做这个方程,\(O(N)\) 的时间进行一次运算。现在再考虑题目中要求最多选 \(k\) 个的限制,我们在方程上再加一维,改成 \(f_{i, j}\) 表示用了 \(i\) 个正方形,覆盖前 \(j\) 条线段时总共覆盖的最少方格数。但同时这个转移的复杂度上升到 \(O(NK)\),而且选多少个这一维斜率优化无能为力的。
注意到题目中有 恰好 / 至多 / 至少 选多少个的限制,这类问题我们试试带权二分。考虑 \(g_i\) 为强制使用 \(i\) 个正方形去覆盖时的最少覆盖数,可以猜测,这个函数是有凸性的。关于严格证明,官方题解是这么说的:

那还是感性理解一下吧:如果 \(k = 1\),那么答案最劣需要覆盖全图。但是如果 \(k = 2\),你可以在 \(k = 1\) 的基础上,把一个右上角的正方形和左下角的正方形(或者为空)去掉,只选择剩下的图形,所以此时一定贪心地选择能去掉大小最多的方案。当然,当没有可去掉的图形时,当前及之后的答案也不再变化。由于 \(g_{i + 1} - g_i\) 代表的就是斜率,即斜率单调非降。那么图象就是个下凸壳了,可以使用 DP 凸优化进行优化。
注意到,从我们刚刚的贪心策略可以看到,此时的 \(g_i\) 还单调不增;这时我们就可以修改函数的定义为 \(g_i = g_i + C \times i\) 其中 \(C\) 是一个固定常数,用一个一次函数,把这个图像往上“翘一翘”,实际上也就是每多使用一个正方形,我们为它增加一个附加权值,让最优答案向左偏移;由于我们之前已经知道了 \(g_i\) 的图像下凸,所以我们这样的操作是符合单调性的。我们再拾起来刚刚设的没有选择个数限制的 DP 方程,只需在每次转移时加上常数 \(C\) 且同时记录使用了几个正方形,就能求出在附加取值为 \(C\) 的情况下的最优解。二分求适当的 \(C\),减去我们的附加权值就是最后的答案。

可是这都什么东西啊
浙公网安备 33010602011771号