[BZOJ 1260][CQOI 2007] 涂色

题目大意

给定一个长为 \(n\) 的字符串表示染色目标,每次只能把连续的一段染成同一颜色,求最少染色次数。

\(1 \leqslant n \leqslant 50\)

题目链接

BZOJ 1260

题解

区间 DP。

\(f[i, \; j]\) 表示区间 \([i, \; j]\) 的答案,转移为: \[ f[i, \; j] = \begin{cases} \begin{align} min(f[i + 1, \; j], \; f[i, \; j - 1], \; f[i + 1,\; j - 1] + 1) &\quad s[i] = s[j] \\ min(f[i, \; k] + f[k + 1, \; j]) &\quad s[i] \neq s[j] \end{align} \end{cases} \] 其中 \(s\) 为目标,答案为 \(f[1, \; n]\)

被数据范围吓到了。。。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <cstdio>
#include <climits>
#include <cstring>
#include <algorithm>
const int MAXN = 55;
int f[MAXN][MAXN];
char s[MAXN];
void dp(int n) {
for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++)
f[i][j] = i == j ? 1 : INT_MAX;
for (int l = 1; l < n; l++) for (int i = 1; i <= n - l; i++) {
int j = i + l;
if (s[i] == s[j]) {
if (l == 1) f[i][j] = 1;
else f[i][j] = std::min(std::min(f[i + 1][j], f[i][j - 1]),
f[i + 1][j - 1] + 1);
} else for (int k = i; k < j; k++)
f[i][j] = std::min(f[i][j], f[i][k] + f[k + 1][j]);
}
}
int main() {
scanf("%s", s + 1);
int n = strlen(s + 1);
dp(n);
printf("%d\n", f[1][n]);
return 0;
}