区间 DP 简单概述
前传:简单线性 DP 详解
区间动态规划是动态规划的一个分支,主要用于解决具有区间性质的问题,特别是涉及到区间的分割、合并或者区间间某种依赖关系的题目。
区间 DP 概念较好理解,实战的话主要是思考难度高一点点。
思路可以总结为八个字:枚举断点,更新区间。
在这点下,和 最短路 Floyd 算法 较为相似。
区间 DP 的状态通常设定为:\(f_{i, j}\) 表示区间 \([i, j]\) 中的一个值。
区间 DP 通常是由两个较小的区间合并答案转移成大区间的答案,因此转移方程通常为:\(f_{i, j} = \max(f_{i, j}, f_{i, k} + f_{k + 1, j} + \dots)\),其中 \(\dots\) 表示可能的”计算更新信息“的方式。
由于区间 DP 的转移通常为从两个较小的区间转移过来,因此我们需要先计算小空间的值,再计算较大空间的值。所以区间 DP 通常最外层循环就需要枚举区间长度。
本题思路
状态:\(dp_{i, j}\) 表示将区间 \([i, j]\) 涂成目标颜色所需要的最少操作次数。
初始化:当区间只有一个字符时,只需要一次涂色操作将这个位置涂成目标颜色。
对于长度大于 \(1\) 的区间 \([i, j]\),可以考虑:
- 假设我们先把区间 \([i, j - 1]\) 都涂好,最后再单独给位置 \(j\) 涂色,这样操作数就是:
-
如果在区间 \([i, j - 1]\) 中存在一个位置 \(k\),满足 \(s_k = s_j\),则说明位置 \(k\) 与位置 \(j\) 的目标颜色相同。
此时,我们可以考虑这样一种策略:
- 先将区间 \([i, k]\) 涂好,使得位置 \(k\) 是正确的颜色。
- 然后涂好部分 \([k + 1, j - 1]\)。
- 最后,由于 \(k\) 和 \(j\) 的颜色相同,我们可以在最后一次操作中“顺带”让位置 \(j\) 也变成目标颜色,从而不需要额外的操作来单独处理 \(j\) 。
我们遍历所有满足 \(s_k = s_j\) 的 \(k\) 值,取其中最小的操作数,即:
我们可以根据长度来递推整个过程。
代码如下:
#include <bits/stdc++.h>
using namespace std;string s;
int n;
int dp[55][55];int main()
{cin >> s;n = s.size();s = " " + s;for (int i = 1; i <= n; i++)dp[i][i] = 1;for (int len = 2; len <= n; len++){for (int i = 1; i + len - 1 <= n; i++){int j = i + len - 1;dp[i][j] = dp[i][j - 1] + 1;for (int k = i; k < j; k++){if (s[k] == s[j]){int x = dp[i][k];if (k + 1 <= j - 1)x += dp[k + 1][j - 1];dp[i][j] = min(dp[i][j], x);}}}}cout << dp[1][n] << "\n";return 0;
}