[记录] dp 优化基础习题
Bitmasks Dp and Dp’s Optimization_翁文涛
DP的决策单调性优化总结_command_block \(\leftarrow\) 没有给相应类型习题去这里找 (搞不完 qwq)
存在乱用 Markdown 的现象
为什么越看越多啊
一本正经地胡说八道
状压 DP
Existence of Hamiltonian Path
一个看起来是模板但是我在哪里都没有找到的状压 DP
给定一副 \(n\) 个点 \(m\) 条边的无向图 \(G\)。问是否存在一个哈密尔顿路径?
哈密尔顿路径指一条恰好经过所有点一次的路径。
\(n\le21,m\le \frac{n^2}{2}\)
设 \(f(S,i)\) 表示已经走过集合 \(S\),目前在 \(i\) 点的路径是否存在,按边转移就好了
CF744C Hongcow Buys a Deck of Cards
题目的难点在于设计什么样的状态,建议自己思考
桌面上有 \(n\) 张红或蓝的卡片。第 \(i\) 张卡片有属性 \((r_i , b_i )\)。
你可以进行任意多次行动,每次可以为以下两种行为:
- 获取 \(1\) 颗红宝石以及 \(1\) 颗蓝宝石。
- 设这次行动前你手上有 \(R\) 张红卡片,\(B\) 张蓝卡片。设桌上还有卡片 \(j\)。你换取这张卡片需要 \(\max(0, r_j − R)\) 颗红宝石以及 \(\max(0, b_j − B)\) 颗蓝宝石。
问你换完所有的卡片最少需要多少次行动?
\(n≤ 16, 0 ≤ r_i , b_i ≤ 10^7\)
思考的时候要有从一个方法不可行的原因尝试推正确做法的意识?
这题的难点在于如何设计状态
- 概率会想用 \(f(S)\) 表示换取的卡片的集合为 \(S\) 时,最少需要行动多少次
- 然后问题是在知道 \(S\) 和 \(f(S)\) 的情况下,并不能推出手上有多少宝石
- 第一,每张卡片受的折扣不好倒推,第二,折扣到 \(0\) 就不会再降低了
- 也就是说已知信息不足以支持 DP 的转移
说明可能状态设的过于简单了
-
再次回到题目,要求的是换完所有的卡片最少需要多少次行动
-
买卡片的行动次数显然是固定的,
那么也就是说要求所花费的红宝石和蓝宝石中较多的那种尽量少
-
而两种宝石是相互独立的,不可能让它们同时最小
所以只能记录一种宝石的情况,令另一种最小,不妨将红宝石的数量也设进状态
设 \(f(S,i)\) 表示表示换取的卡片的集合为 \(S\) ,用了 \(i\) 个红宝石时,最少需要的蓝宝石的数量
-
现在的问题在于这样状态数就到了 \(O(2^n\times10^7)\) 的级别,考虑优化状态
因为已知所换取的卡片,所以只要知道获得了多少的折扣即可推出实际花费的宝石数量
而折扣的数量很小,是 \(O(n^2)\) 级别的,用节省了多少红宝石来替代花费了多少红宝石,这样 \(O(2^n\times n^2)\) 的状态 DP 就好了
Clear The Matrix
比较好想,可以用来练习状压 DP
给定一个 \(4 × n\) 的元素只为 ∗ 或 ′ . ′ 的矩阵 \(f\) 。
你可以不断地选择一个 \(f\) 的子方阵,并将方阵的元素都变为 ′ . ′
选择一个 \(k × k\) 的方阵需要代价 \(a_k\) 。
问最少要多少代价,才能将所有元素都变为 ′ . ′\(4 ≤ n ≤ 1000, 1 ≤ a_i ≤ 1000\)
状态应该不算难想,因为最大的可选择范围是 \(4\times4\) 的,所以可以用 \(f_{i,S}\) 表示前 \(i\) 行合法,接下来 \(4\) 行的状态。
然后枚举怎么覆盖来转移。
如果不能思路清晰地预处理,可能实现的时候会比较难受。
最好预处理一下每个大小的子方阵在 \(4\) 个位置的覆盖范围。
状压 DP 有时候会出现理论复杂度很大但是合法状态不多的情况,这时候可以适当剪枝。
转移优化?
Transferring Pyramid
应该是 ppt 里最难的一题
给定一个 \(n\) 层的金字塔,一开始塔上每个位置都没有染色。你可以进行两种操作:
- 给某一特定格子染色,代价为 \(3\)
- 给一个子金字塔(必须到最底层)染色,代价为 \(2+\) 大小。
给定 \(k\) 个关键位置。问将这 \(k\) 个位置都染上颜色的最小代价。
\(n,k\le10^5\)
如果先去做「IOI2016」aliens 这题,应该会想到将这个金字塔顺时针转 \(45\) °,然后重标号,再按顺序考虑。
对于每一行,肯定存在一个非负整数 \(k\),第 \(k\) 列之前用的是子金字塔染色的方式,之后是单个染色的方式。
因为一行最多只会选择一个位置为子金字塔的塔顶,可以根据塔顶的位置 DP。
\(f_{i,j}\) 表示第 \(i\) 行选择的塔顶是 \(j\),这一行的关键位置都覆盖的最小代价。
\(sum_{i,j}\) 表示第 \(i\) 行,\(j\) 后缀的关键位置数量。
对于这一行不覆盖子金字塔的情况
\(f_{i,0}=f_{i-1,j}+sum_{i,j+1}\times3\)
顺延覆盖的情况
\(f_{i,j}=f_{i-1,j+1}+sum_{i,j+1}\times3\)
非顺延覆盖的情况
\(f_{i,j}=\min \{f_{i-1,k}+\frac{j\times(j+1)}{2}+sum_{i,j+1}\times3\}\)
前缀和优化复杂度 \(O(n^2)\)
接下来的优化大概叫最优化剪枝 ?
从贡献的计算方式看,会有这样一个想法,如果选择的子金字塔太空,会划不来。
这说明如果选择的子金字塔太大了,还不如直接单点覆盖,经过计算发现大概边长超过 \(\sqrt{6k}\),就划不来。
于是状态数降到了 \(O(n\sqrt{k})\) 级别,前缀和优化成 \(O(1)\) 转移就好了。
倍增FFT 优化
PolandBall and Many Other Balls
需要一些生成函数知识,有多种解法,这里只给出和 DP 关系最近的一种。
现在有 \(n\) 个排成一列的有编号的球。
你要选恰好 \(m\) 组球,每组球要么只有 \(1\) 个,要么是两个相邻的球。
每个球最多只能在一组内。球可以不在任何组。
给定 \(n\), \(k\) ,问对于 \(m ∈ [1, k ]\),合法的分组方案对 \(998244353\) 取模的值。\(n\le10^9,k<2^{15}\)
最简单的想法是设 \(f(i,j)\) 表示在前 \(i\) 个球中选 \(j\) 个球,并且选了第 \(i\) 个求的方案数
那么 \(\sum_{i=1}^n f(i,m)\) 即 \(n\) 个球中选 \(m\) 个球的方案,然后打表发现 DP 式子可以更简单
1 0 0 0 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 0
1 3 1 0 0 0 0 0 0 0
1 5 5 1 0 0 0 0 0 0
1 7 13 7 1 0 0 0 0 0
1 9 25 25 9 1 0 0 0 0
1 11 41 63 41 11 1 0 0 0
1 13 61 129 129 61 13 1 0 0
1 15 85 231 321 231 85 15 1 0
1 17 113 377 681 681 377 113 17 1
设 \(f(i,j)\) 表示在前 \(i\) 个球中选 \(j\) 个球的方案数
\(f(i,j)=f(i-1,j)+f(i-1,j-1)+f(i-2,j-1)\)
其实就是对应三种情况,不选新增的 \(i\) 球,\(i\) 球单独一组和 \(i\) 和 \(i-1\) 两个一组
先将转移方程式写成生成函数的形式 (一行为一个生成函数)
\(F_n(x)=(1+x)F_{n-1}(x)+xF_{n-2}(x)\)
这样只能从 \(n-1\) 推到 \(n\),显然在复杂度上满足不了我们
考虑能否从 \(n\) 和 \(m\) 转移到 \(n+m\) (似乎这才是倍增 FFT 优化所需要的条件)
还是先以 DP 转移式的角度去考虑怎么把两者接起来得到 \(n+m\) 的情况
比较显然的是直接放一起不相互干涉:\(f(n+m,k)=\sum_{i=0}^kf(n,i)\times f(m,k-i)\)
另一种情况是最中间两个球为一组:\(f(n+m,k)=\sum_{i=0}^{k-1}f(n-1,i)\times f(m,k-1-i)\)
当 \(n=m\) 时,就可以得到 \(F_{2n}(x)=F_n^2(x)+xF_{n-1}^2(x)\),从 \(n\) 和 \(n-1\) 推到 \(2n\)
然后因为从 \(n-1\) 推到 \(n\) 的时候需要维护相邻的两项,由此都写出来
现在就可以从 \(F_{n-2}(x),F_{n-1}(x),F_{n}(x)\) 转移到 \(F_{2n-2}(x),F_{2n-1}(x),F_{2n}(x)\)
再加上线性转移,就符合倍增 FFT 的基本条件
实现上一个 \(\mathtt{Trick}\) 是为一个多项式乘 \(x\) 在点值下相当于对第 \(i\) 位乘 \(\omega_{n}^i\)
到这里我才明白 wwt 为什么要把这个叫做 \(\text{×2 + 1 Trick}\)
最开始是只能 \(f_n\rightarrow f_{n+1}\),故转移是 \(O(kn)\) 的
现在可以还可以 \(f_n\rightarrow f_{2n}\),转移就降到 \(O(k\log^2 n)\) ,转移次数取 log,再多一个 FFT的 log
所以倍增 FFT 的关键应该是要能同时实现这两种转移
于是就讲完了最慢的倍增 FFT 做法,还存在快很多的特征方程,组合容斥的解法,咕咕咕
决策单调性优化
一般根据四边形不等式、打表等得到决策单调性的结论
-
根据四边形不等式确定决策点的区间范围,多用于区间 DP \(O(n^3)\rightarrow O(n^2)\)
-
分治确定决策点,要求相互之前不影响。
有时候计算转移需要用到指针维护,复杂度均摊的技巧
-
用队列 or 栈维护决策点,然后不同决策点对应的分界点通过二分得出
-
cmd 还提到了分k段问题,段数 +1,形成的决策路径必然交错的观点,可以看看
「POI2011」Lightning Conductor
题目比较简单,可以算决策单调性的入门题,用分治维护比较无脑,建议练习用栈维护
给定一个长度为 \(n\) 的序列 \(a_i\) ,对于每个 \(i\),求出 \(⌈\max(a_j +\sqrt{|i − j|}) − a_i ⌉\)
\(n\le10^5\)
写的时候已经知道是决策单调性优化了,不过还是写了个 \(n^2\) 验证一下
其实这个题决策单调性还比较好推
考虑 \(y=\sqrt x\) 的函数图像,不难发现当随着 \(i\) 逐渐变大,在某一刻 \(j\) 比 \(k\ (k < j)\) 更优了,那么 \(k\) 往后不可能再比 \(j\) 更优
但是验证的时候非常迷惑地发现决策点不是单调的,后来意识到不能在转移过程中就上取整,会产生误差
Optimal Binary Tree Problem
套路题
有 \(n\) 个元素,编号为 \(1\) 到 \(n\)。编号 \(i\) 的元素期望会被查询 \(q_i\) 次。
请构造一棵二叉树 \(T\) ,满足 \(T\) 的中序遍历为 \(1 → n\)。
设 \(dep_i\) 为编号为 \(i\) 的元素在 \(T\) 上的深度。则 \(T\) 的期望总查询次数为:\(\sum_{i=1}^n q_i\times dep_i\)
请最小化这个值。
\(n\le5000\)
设 \(f(i,j)\) 表示把 \([i,j]\) 建成二叉搜索树的最小期望总查询次数,令 \(s(i)=\sum_{j=1}^iq(i)\)
\(f(i,j)=\min\lbrace f(i,k-1)+f(k+1,j)+s(j)-s(i-1)\rbrace\) 枚举根节点转移
之所以说套路,是因为相当于是四边形不等式优化 DP 的板子
唯一的问题是考到四边形不等式优化的时候可能只能猜是不是满足这样的性质
斜率优化
在满足可以斜率优化的基础上,根据数据的特性,有几种维护方式
①插入斜率有序 ②询问 \(x\) 点单增
-
同时满足①②:单调队列维护凸壳,每次取队首
-
仅满足①:单调队列维护凸壳,在上面二分
-
啥都不满足:
-
平衡树 (似乎可以用 set,可以问 zhy 怎么实现,但是好像很难写)
-
李超树 (似乎不难写,但我不会 qwq)
-
CDQ 分治 (目前只会这一种)
-
「APIO2010」特别行动队
最简单的一道
有 \(n\) 个士兵排成一列,第 \(i\) 个士兵战斗力为 \(x_i\) 。你要将士兵分成若干连续的组。
给定 \(a\), \(b\), \(c\),对于一个 \([s, t]\) 的组,设 \(s=∑_{i=s}^tx _i\) ,其权值为 \(as^2 + bs + c\)。
问总权值最大可以是多少。
\(1 \leq n \leq 10^6\),\(5 \leq a \leq -1,-10^7 \leq b \leq 10^7-10^7 \leq c \leq 10^7,1 \leq x_i \leq 100\)。
斜优。
拆迁队
题目比较简单,可以作为斜率优化练习题
给定一个长度为 \(n\) 的数列 \({a_i }\)。你希望把这个数列变成一个严格递增的数列。
每个位置 \(i\),你可以将他修改为一个新的数,也可以不修改,但这样需要花费 \(b_i\) 。
设最终的数列为 \({c_i }\),这个数列需要满足 \(c_i < c_{i+1} , 0 < c_i\) 。你的总花费就是 \(\sum_{i=1}^n c_i\) 加上不变的位置的 \(b_i\) 的和。
你的目标是,在最大化不变位置数目的同时,最小化总花费。
$ 1 ≤ n ≤ 10^5 , 1 ≤ a_i , b_i ≤ 10^7$
第一问将每个位置上的数转化成 \(a_i-i\),就变成了最长不下降子序列问题
第二问就分层转移一下,用 CDQ 分治进行斜率优化
wqs 二分
- 优化一些特殊的费用流模型 (在洛谷日报上看到的,具体先咕咕咕)
- 用 \(O(\log)\) 的时间替换 \(O(k)\) 的空间
「IOI2016」aliens
这题应该大概是不到黑题难度,思维难度不算高,只是代码上实现方式会导致一些细节问题
有一个 \(m × m\) 的正方形网格,其中 \(n\) 个关键点。
可以用不超过 \(k\) 个小正方形去覆盖它们。
要求小正方形的主对角线在大正方形的主对角线上,且面积并最小。
\(1 ≤ n ≤ 10^5 , 1 ≤ m ≤ 10^6\)
在网格图上 DP 听起来有点麻烦,但是去除冗杂信息后就简单了。
比如说其实可以把对角线左下方的关键点对称到右上方。
先来裸的 DP,设 \(f(i,j)\) 表示前 \(i\) 个关键点用了 \(j\) 个正方形。
转移上的主要问题就是如何处理面积并,考虑若已知上一个正方形覆盖的最后一个关键点,能否推出将后面一些点用正方形覆盖,两个正方形的面积并是多少。
不难发现正方形的边不是贴着它所覆盖的最后一个关键点的情况肯定是之前存在一个点,覆盖那个点的所有正方形都可以覆盖到最后一个点,不妨去掉这种一定会和其他的点一起覆盖的点。
然后就可以 wqs 二分 + 斜率优化了
有两个细节 我写的时候挂了不止两个细节
wqs 二分时,面积并最小的情况下,要正方形数量尽量小,这样得出的函数才是有凸性的
题目数据较强,比大小的时候可能需要 eps
The Monge Speedup
pdf 讲得很简单,网上几乎什么也找不到,听说 apio 讲过,咕咕咕