[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 <climits>
#include <cstring>
#include <algorithm>
const int MAXN = 55;
int f[MAXN][MAXN];
char s[MAXN];
void (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("%dn", f[1][n]);
return 0;
}