区间 dp 总结
写在前面:挺好玩的,就是有点费脑子。
区间 dp,顾名思义,其本质是在大区间内枚举断点,将其分成两个小区间后选择最优方案进行转移。
特点为从小区间转移至大区间,自底向上求值。
通项公式为 \(f_{l,r}=\max\limits_{k=l}^{r-1}f_{l,k}+f_{k+1,r}+w_{l,r}\)。
主要 dp 板块需要借助题目感性理解一下。不然会很抽象。虽然借助例题理解也很抽象就是了
下方由主观难度由易到难排序。
过于简单的就不写了。主要是懒
T1. [CQOI2007] 涂色
设状态 \(f_{l,r}\) 为 \([l,r]\) 内的最少涂色次数。
初始化 \(f_{i,i}=1\), \(f_{i,i+1}=\begin{cases}2&s_i\neq s_{i+1}\\1&s_1=s_{i+1}\end{cases}\) 。
显然,若两次涂色有重合部分,必然不是最优解。
枚举断点 \(k\),有转移方程 \(f_{l,r}=\min\limits_{l+1}^{r-1}f_{l,k}+f_{k+1,r}\)。
特判一下 \(s_l=s_r\) 的情况。 此时可以发现 \(f_{l,r}\),\(f_{l+1,r}\),\(f_{l,r-1}\) 等价。即 \(f_{l,r}=f_{l+1,r}=f_{l,r-1}\)。
故
T2. 表达式的亿种可能性
令 \(A\) 为 \(f_{l,k}\) 所有运算顺序结果的集合,\(B\) 为 \(f_{k+1,r}\) 所有运算顺序结果的集合。令 \(|A|=p,|B|=q\)。
故
即
由于 \(f_{l,r}=\{A_1,A_2,\cdots A_p\}op_k\{B_1,B_2,\cdots B_q\}\),即只要保证 \(A,B\) 的内部顺序不变,可以将 \(A,B\) 任意穿插,共有 \(C^p_{p+q}\) 种顺序。
故要给 \(f_{l,r}\) 乘上系数 \(C^p_{p+q}\)。
时间复杂度 \(O(n^3)\)。
T3. [JXOI2018] 守卫
这题主要是状态转移的细节不太好理解。
但是是第一道单切紫题,好诶 所以就把题解粘上来了
设状态 \(f_{l,r}\) 为在区间 \([l,r]\) 内要放的最少保镖数量。
看到题面第一眼的感觉是不会判两点能否连接。
第二眼发现可以用斜率判。
令 \(k_{l,r}\) 为横坐标为 \(l,r\) 的两点连线斜率。
有 \(k_{l,r}=\frac{h_r-h_l}{r-l}\)。
手搓几组样例,得 \(\forall x\in[l,r)\),有 \(k_{l,r}<k_{x,r}\) 。即对于 \(x\in[l,r)\),\((x,r)\) 的连线斜率最小。
且若 \(a\) 对于 \(b\) 可见,并满足 \(h_c>h_b\) 与 \(c>a\),则 \(a\) 对于 \(c\) 可见。
令 \(p=\{p_1,p_2\cdots p_m\}\) 为 \(r\) 点当前能够覆盖的点集。注:下文中 \(p\) 按照从右至左斜率单调递减的顺序排序。
注:能被覆盖的点可能不连续。但能被覆盖的点的斜率可以保证从右至左单调递减。
若想为点集连续,可能会导致 “\(p\) 为 \(r\) 点当前能够覆盖的最远点” 的错误贪心思路。
喜提20pt。
如图,\(r=G\) 时,\(p=\{E,D,B\}\)。
接下来是这题个人认为比较难处理的地方。
由于 \(p\) 不一定连续,故 \([p_{i+1}+1,p_i-1]\) 对于 \(r\) 不可见。
由上述性质得,对于 \(j\in[p_i,r)\),\(j=p_i\) 是唯一可以覆盖 \([p_{i+1}+1,p_i-1]\) 中至少一个点的点。 即在区间 \((p_{i+1},p_i)\) 右侧唯一能够覆盖到此区间的点为 \(p_i\)。(有点绕,建议自己手玩一下。)
如图,以 \(A\) 为右端点,\([D,A]\) 中能覆盖 \([G,E]\) 的唯一点为 \(D\)。
当然,也可以选择在 \(p_i-1\) 处放置保镖,从内部将点覆盖。
如在点 \(E\) 放置保镖。
故 \(p_i,p_i-1\) 至少有一处要放置保镖。
这样就成功的将区间 \([p_{i+1}+1,p_i]\) 或 \([p_{i+1}+1,p_i-1]\) 转换为了一个子问题。
区间 \([p_{k+1}+1,p_k-1]\) 的代价即为
\[f_{p_{i+1}+1,p_i-1}=\min\{f_{p_{i+1}+1,p_i-1},f_{p_{i+1}+1,p_i}\} \]
此时有转移方程:
时间复杂度 \(O(n^3)\),能拿 \(70\) pts。
考虑优化。
-
因为 \(p_i\) 是在 \([p_i,r)\) 中与 \(r\) 点连线斜率最小的,所以对于 \(x\in[l,p_i)\),若存在 \(f_{x,r}<f_{p_i,r}\),则 \(p_{i+1}=x\)。 可以考虑将 \(p_i\) 在循环里滚动处理。
-
可以注意到根据上述转移,当处理至 \(p\) 点时,区间 \([p,r]\) 可以保证被完全覆盖。有转移方程
\[f_{l,r}=\min\{f_{l,p-1,f_{l,p}}\}+f_{p+1,r} \]
干掉一个线性时间复杂度。时间复杂度 \(O(n^2)\)。
T4. 有味道的数字
先写一个暴搜。发现 \(n\leq 5000\) 的时候,\(|P|\leq 11\)。
即 \(n\leq 5000\) 的所有数字可以用 \(11451419191\) 表示。
这题主要是状态设计难想。
设 \(f_{l,r}\) 为区间 \([l,r]\) 能表示的所有数的集合。
令 \(A=\{1,1,4,5,1,4,1,9,1,9,1\}\)。
初始化 \(f_{l,r}\) 为 \(A_l\) ~ \(A_r\) 直接拼接的数字。如 \(f_{3,6}=\{4514\}\)。
显然有状态转移:
统计答案时,\(ans_x=\min\limits_{x\in f_{1,i}}i\)。
T5. [HAOI2016] 字符合并
然鹅并没有过。但是可以口胡一下。
看到二进制,一眼状压。
令 \(f_{l,r,s}\) 为区间 \([l,r]\) 合并为 \(s\) 获得的最大累计分数。
毕竟你得满足无后效性对吧。当区间内有多种情况或多条限制时,可以考虑加维转移。
那么对于每个状态 \(s\),可以枚举断点 \(i\) 使得
显然复杂度 \(O(2^{k-1}n^3)\) 会爆炸。
显然对于区间 \([l,r]\),最终合并长度为
所以只需要枚举 $k-1\ |i\ $ 的情况。
从合并后的串来转移。有转移方程
当 \(L=1\) 时,可以直接合并。
时间复杂度 \(O(\frac n k\times2^{k-1}n^2)\)。
常见 Tricks
- 遇到消除类的区间 dp,【数据删除】。
- 先这样吧,找时间填坑。
结语
个人认为区间 dp 是 dp 专题里面最简单的了。
主打一个感性理解和经验积累。真没啥套路。
想得出来就好写,想不出来就开摆。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效