P4170 [CQOI2007] 涂色
注意到两次涂色的区间要么不相交,要么一个包含另一个。如果两个区间只有一部分相交,那我们可以把左侧区间的右端点左移,使其不相交而不改变涂色结果。
基于这个结论,我们考虑区间 dp。设 \(f_{l,r}\) 表示将区间 \([l,r]\) 涂成目标颜色的最小操作数,从 \(f_{l,r-1}\) 转移到 \(f_{l,r}\)。如果
- \(s_l=s_r\),我们发现 \(l\) 位置一定只被涂过一次(因为它是区间端点),将这一次涂色变成涂 \([l,r]\) 并放到第一次涂,即可在不增加涂色次数的情况下达成目标,转移方程为 \(f_{l,r}=f_{l,r-1}\)。
- \(s_l\neq s_r\),说明一定可以找到一个分割点,使得没有涂色操作横跨左右两边。用 \(f_{l,k}, f_{k+1,r}\) 更新 \(f_{l,r}\) 即可。
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100;
char s[N];
int f[N][N];
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
memset(f, 0x3f, sizeof(f));
for (int i = 1; i <= n; i++) f[i][i] = 1;
for (int len = 2; len <= n; len++) {
for (int l = 1; l + len - 1 <= n; l++) {
int r = l + len - 1;
if (s[l] == s[r]) {
f[l][r] = min(f[l - 1][r], f[l][r - 1]);
continue;
}
for (int k = l; k < r; k++) {
f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r]);
}
}
}
printf("%d\n", f[1][n]);
return 0;
}

浙公网安备 33010602011771号