区间DP总结
前言:
写了几道区间DP,略有感悟:
常规的区间DP其实是一种较为模板的东西
首先就是 数据范围一般为 500 ~ 700,也只有在这个范围内才能做到
O
(
n
3
)
O(n^3)
O(n3) 的复杂度来解决问题。
其次就是写法也较为模板,基本为三重循环的嵌套:
- 最外层循环枚举 l e n len len,也就是区间长度从小到大;
- 第二层循环枚举左边界 l l l,然后可以根据 r = l + l e n − 1 r = l + len - 1 r=l+len−1 直接计算出右边界;
- 第三层循环枚举分界点 k,也就是大区间是由哪两个小区间拼凑起来的。
之后就是不同题目不同处理了。
Codeforces Round 336 (Div. 1) B. Zuma
- 知识点:回文串,区间DP
根据数据量我们可以锁定区间DP,然后就是思考状态转移公式:
f
[
l
]
[
r
]
f[l][r]
f[l][r] 表示
l
l
l ~
r
r
r 的区间全部删除的最小次数。
思考状态转移:
- 首先根据三板斧,我们可以推出, f [ l ] [ r ] = m i n ( f [ l ] [ k ] + f [ k + 1 ] [ r ] , f [ l ] [ r ] ) f[l][r] = min(f[l][k] + f[k + 1][r], f[l][r]) f[l][r]=min(f[l][k]+f[k+1][r],f[l][r])
- 题目中提到如果是回文串,则可以一步删除,由于 l l l ~ r r r 的区间都已经删完了(所以 l l l ~ r r r 就是一个回文串),所以只要 s [ l − 1 ] s[l - 1] s[l−1] = s [ r + 1 ] s[r + 1] s[r+1] 那么 l − 1 l - 1 l−1 ~ r + 1 r + 1 r+1 是一个回文串,所以我们的状态还可以这样转移: f [ l ] [ r ] = m i n ( f [ l + 1 ] [ r − 1 ] + 1 , f [ l ] [ r ] ) f[l][r] = min(f[l + 1][r - 1] + 1, f[l][r]) f[l][r]=min(f[l+1][r−1]+1,f[l][r])
第二种情况也可以理解为,上一步删除的那部分小回文串可以连同更大的回文串一并删除,那么这步操作的贡献上一步已经一并计算过了,就可以优化掉一步。
这里把初始化写在一起了
void solve()
{
int n;
cin >> n;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
vector<vector<int>> f(n + 1, vector<int>(n + 1, INF));
for (int len = 1; len <= n; len++) {
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if (len <= 2 && a[l] == a[r]) {
f[l][r] = 1;
} else {
if (a[l] == a[r]) f[l][r] = f[l + 1][r - 1];
for (int k = l; k < r; k++) {
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
}
}
}
}
cout << f[1][n] << endl;
}