区间 DP 总结
_RainCappuccino_ · · 个人记录
观前提醒: 此博客仅为我对区间 DP 的个人观点,如有错误或是误解,请您见谅或是提出问题,我将会积极改进此博客。
这玩意儿上限很高,
下限也很高做区间 DP = 烧脑子
\color{green}\mathtt {Level.1}\quad \mathtt{Easy Problem}
较为简单的区间 DP 一般状态设计较为简单,转移也仅仅只有区间分开再合并或是通过一定条件从该区间自身包含的区间着手,相当于按照题意进行模拟。
状态: 一般
转移:
-
dp[i][j] = dp[i - 1 / i][j - 1 / j]\; someopt \; something -
dp[i][j] = dp[i][k] \; someopt \; dp[k + 1][j]
特点:
- 一般是一个环
- 或者是一个双端队列
- 或者是一个栈
Eg.1
P1063 [NOIP2006 提高组] 能量项链
特点: 环
此题可以说是区间 DP 入门题,状态:
dp[i][j] 表示合并[i,j] ,能得到的最大价值。 转移: 对于一个区间:[l, r] ,考虑它的合并情况:先合并[l, k] 和[k + 1, r] ,再将这两个区间合并在一起,就能得到一种合并区间[l, r] 的方案,由于dp[l][k] 和dp[k + 1][r] 都是最优情况下的合并,无需再去管它,所以dp[i][j] 取\max(dp[l][k] + dp[k + 1][r] + w[l] * w[k] * w[r]) 即可Eg.2
P1040 [NOIP2003 提高组] 加分二叉树
特点: 模型转化
此题看似是一道图论的题,实际上由于中序遍历为 :
1,2,3,……,n。 可以将dp[i][j] 设为 节点i 到节点j 可生成的子树最大价值为多少。 由于中序遍历根节点的左边一段为左子树,右边一段为右子树,所以考虑枚举该子树的根节点,所以dp[i][j] = dp[i][k - 1] \times dp[k + 1][j] + a[k] 。这同样可以理解为一个区间 DP。
\color{blue}\mathtt {Level.2}\quad \mathtt{Hard Problem}
较难的区间 DP,相较于简单区间 DP 的难度提升主要有两种:
- 状态设计:
对于一个区间,状态中不光为一个区间的左右端点,可能还要记录一些这个区间的特性。如
dp[i][j][1] 和dp[i][j][2] 可能进行的转移不同。
- 转移:
此类问题中转移会涉及到一些复杂的判定能否转移,或者是转移时运用一些组合数学的知识。
感觉例题我讲不明白。
例一:[SCOI2003] 字符串折叠
P4302 [SCOI2003] 字符串折叠
难点: 转移
先考虑最朴素的状态设计
转移:
-
区间分段,然后合并。显然,两个区间之间并不会互相影响,所以可以这么转移。
-
考虑折叠。由于此题数据范围很小,可以暴力枚举循环节,看看哪个可行,然后这个区间就可以压缩成 数字位数 + ( + 循环节长度 + ) 长度。但是观察样例可以发现:折叠是可以嵌套的,所以循环节仍然可以折叠,所以中间就不应该是循环节长度,而是
dp[ \mathsf{循环节}] 。
例二:表达式的亿种可能
表达式的亿种可能
给定一个表达式,
每次你可以取出任意两个相邻的数字与它们之间的运算符
num_i ,opt_i,num_{i+1} ,优先进行计算再把运算结果放回。在进行了
(n - 1) 轮上述操作后,可以得到该表达式的值。容易发现共有
(n - 1) ! 种可能的计算顺序,求所有计算顺序得到的表达式的值之和。难点: 转移
此题状态还是那个朴素的
注意
- 对于最终长度等于
1 的情况不能转移,所以转化为倒数第二个状态,最终再一合并转化回来,加上当前k 位状态的值。
\color{gray}\mathtt {To \; sum \; up}
区间 DP,几乎没什么套路,全靠思维灵活度。