题目描述
假设你有一条长度为5的木版,初始时没有涂过任何颜色。你希望把它的5个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为5的字符串表示这个目标:RGBGR。
每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成RRRRR,第二次涂成RGGGR,第三次涂成RGBGR,达到目标。
用尽量少的涂色次数达到目标。
输入格式
输入仅一行,包含一个长度为n的字符串,即涂色目标。字符串中的每个字符都是一个大写字母,不同的字母代表不同颜色,相同的字母代表相同颜色。
输出格式
仅一行,包含一个数,即最少的涂色次数。
输入输出样例
输入 #1
AAAAA
输出 #1
1
输入 #2
RGBGR
输出 #2
3
说明/提示
40%的数据满足:1<=n<=10
100%的数据满足:1<=n<=50
分析
首先,这道题为什么是区间dp呢?
注意到,我们涂色不可能使两个互不包含的区间相交,因为相交部分会被覆盖。
因此一个大的区间一定是由两个独立的子区间合并而来,符合区间dp的性质。
但是我有一个问题想了挺久,就是先一个颜色直接贯穿的情况怎么破?
eg. n = 5, s = 35423
其实这种情况要满足左端点 = 右端点,有一边可以免费涂,而不用拆成独立的两部分(不然会WA!
转移方程如下:
if(s[i]==s[j]) f[i][j]=min(f[i+1][j],f[i][j−1])
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]) k∈[i,j)
代码如下
#include <bits/stdc++.h>
using namespace std;
char s[55];
int f[55][55];
int main(){
int i, j, k, n, m;
scanf("%s", s + 1);
n = strlen(s + 1);
memset(f, 1, sizeof(f));
for(i = 1; i <= n; i++) f[i][i] = 1;
for(i = n; i >= 1; i--){
for(j = i + 1; j <= n; j++){
if(s[i] == s[j]) f[i][j] = min(f[i+1][j], f[i][j-1]);
for(k = i; k < j; k++) f[i][j] = min(f[i][j], f[i][k] + f[k+1][j]);
}
}
printf("%d", f[1][n]);
return 0;
}