AtCoder DP Practice
众所周知,AtCoder 有一场极 educational 的 DP Contest。
A
裸的线性 DP,设 \(dp_i\) 表示跳到第 \(i\) 个格子的最小费用,显然有转移:
边界为 \(dp_1 = 0, dp_2 = |h_1 - h_2|\)。
B
裸的线性 DP,设 \(dp_i\) 表示跳到第 \(i\) 个格子的最小费用,显然有转移:
边界为 \(dp_1 = 0\)。
C
裸的线性 DP,设 \(f_{i, 0}, f_{i, 1}, f_{i, 2}\) 分别为第 \(i\) 天选择 \(a, b, c\) 活动的最大值,显然有转移:
答案为 \(\min(dp_{n, 0}, dp_{n, 1}, dp_{n, 2})\)。
D
裸的 01 背包,二维太裸了,直接滚动数组。设 \(dp_i\) 表示处理到当前物品时背包容量为 \(i\) 的最大价值,得:
注意要从 \(w \to w_i\) 枚举才能控制每个物品进入 \(1\) 次,否则就是完全背包。
E
传统 01 背包装肯定要爆的,但是注意到虽然 \(w_i\) 大,但是 \(v_i\) 小,可以不是很自然地想到交换价值和体积,即设 \(dp_{i, j}\) 表示考虑前 \(i\) 个物品,达到 \(j\) 价值所需的最小体积,得到类似 01 背包的转移:
维护总价值,从大到小枚举总价值,找到第一个满足 \(dp_i \leq m\) 的 \(dp\) 值输出其 \(i\) 即可。
F
暴力 LCS 转移是一个 DP 初心者也可以轻松设计出来的状态,设 \(dp_{i, j}\) 表示考虑 \(s\) 的前 \(i\) 个字符和 \(t\) 的前 \(j\) 个字符的 LCS 长度,则有转移:
通过判断转移至 \(i, j\) 是否增加来保存最优路径。
G
优美的有向无环图,每个点跑一次 DP,\(O(n)\) 解决问题。
H
简单的“数字三角形”类的 DP,设 \(dp_{i, j}\) 表示走到 \((i, j)\) 的方案数,显然有转移:
corner case 处理可以聪明一点,我们在 \(dp_{0, 1}/dp_{1, 0}\) 设为 \(1\),然后 \(i = 1 \to h, j = 1 \to w\) 遍历直接转移就能少掉边界的讨论。
I
概率 DP,还是那句话,没什么头绪就考虑把答案作为 DP 状态。
设 \(dp_{i, j}\) 表示前 \(i\) 个硬币中 \(j\) 个正面朝上的概率,转移算算就推出来了(其中 \(p_i\) 为题目给定的正面朝上的概率):
反面朝上的概率为:
正面朝上的处理也挺简单:
注意到可以滚掉 1 维,但是没必要。
J
写一次忘一次的期望 DP,可以看看 这个 (doge,当时我怎么写明白的,太牛了
K
一眼的结论:若剩余 \(0\) 个石子则当前游戏者必败。
设 \(dp_i\) 为剩余 \(i\) 个石头时的胜负情况,只要能转移到一个必败状态那就能 win 了,暴力 for 枚举就能做。
L
类似区间 DP,少了断点枚举。设 \(dp_{l, r}\) 表示当前游戏方在 \(l, r\) 内能取到的最大值,则有转移:
corner case 显然是 \(dp_{i, i} = a_i\)。维护前缀和 \(O(1)\) 转移即可。
M
前缀和优化,设 \(dp_{i, j}\) 表示前 \(i\) 个数加起来和为 \(j\) 的情况,通过枚举最后一个数显然有转移:
边界为 \(dp_{1, j} = [j \leq a_i]\)。
暴力转移显然 T,但是这个东西可以上个前缀和优化:
记:
可以改写转移了:
N
只能合并相邻项的石子合并,设 \(dp_{i, j}\) 表示合并 \(a_i \sim a_j\) 的最小代价,则有转移:
边界就比较常规了,初值为 \(inf\),其中 \(dp_{i, i} = 0\)。
O
状压 DP,考虑能转移的条件:
-
当前集合中男女生相容
-
当前男女生尚未匹配
设 \(dp_{i, j}\) 为 \(i\) 个男生和女生构成的集合 \(j\) 的匹配数量,设 \(S_j\) 表示集合中已经有的女生情况,显然枚举 \(i\) 的过程中需要保证 \(S_j = i\),然后枚举另一个女生满足上述两个条件。写个裸转移:
P
Easy 树形 DP。类似没有上司的舞会,设树根为 \(1\),当前节点为 \(u\),设 \(dp_{u, 0}/dp_{u, 1}\) 表示当前节点染白/黑。则根据乘法原理显然有转移:
Q
是不是挺典的。设 \(dp_i\) 为前 \(i\) 朵花满足“高度单调递增”的情况下权值的最大值,转移为:
这个裸的是 \(O(n^2)\) 的,但是上个数据结构比如 Fenwick/ Segtree 来查 \(dp_1 \sim dp_{i - 1}\) 的 \(\max\) 就能拿下了。
R
我们都会有一个 naive 的第一想法:设 \(dp_{i, j, k}\) 表示从 \(i\) 走 \(k\) 步到 \(j\) 的方案数,这个转移非常 easy,但是是 \(O(n^3 k)\)。
考虑设 \(dp_{i, j}^{k}\) 表示从 \(i\) 走 \(k\) 步到 \(j\) 的方案数,然后我们通过 Floyd 枚举中转点的思路就有转移:
然后这个东西是可以上矩乘优化的,答案即为矩阵的 \(k\) 次方中所有数的和,复杂度压进了 \(O(n^3 \log k)\)。