题目描述
假设你有一条长度为5的木版,初始时没有涂过任何颜色。你希望把它的5个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为5的字符串表示这个目标:RGBGR。
每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成RRRRR,第二次涂成RGGGR,第三次涂成RGBGR,达到目标。
用尽量少的涂色次数达到目标。
1≤n≤501\le n\le 501≤n≤50。
算法分析
经典的区间DP,设 f[i][j]f[i][j]f[i][j] 表示区间 [i,j][i,j][i,j] 的最少染色次数,则:
- 当左右两端颜色相同时一次涂色可以从另一端点延伸过来或者重新涂色,有 f[i][j]=min{f[i+1][j−1]+1,min{f[i+1][j],f[i][j−1]}}f[i][j]=\min\{f[i+1][j-1]+1,\min\{f[i+1][j],f[i][j-1]\}\}f[i][j]=min{f[i+1][j−1]+1,min{f[i+1][j],f[i][j−1]}};
- 当左右两端颜色不同时可以将区间拆成两部分分别染色,有 f[i][j]=min{f[i][k],f[k+1][j]}f[i][j]=\min\{f[i][k],f[k+1][j]\}f[i][j]=min{f[i][k],f[k+1][j]};
- 边界条件为区间大小为 111 时最少染色次数是 111。
时间复杂度 O(n3)O(n^3)O(n3)。
代码实现
#include <cstdio>
#include <cstring>
#include <algorithm>
#define UPD(x,y) x=std::min(x,y)
int n,f[55][55];char s[55];
int main() {
scanf("%s",s+1);n=strlen(s+1);
memset(f,0x3f,sizeof(f));
for(register int i=1;i<=n;++i) f[i][i]=1;
for(register int l=2;l<=n;++l) {
for(register int i=1,j=i+l-1;(j=i+l-1)<=n;++i) {
if(s[i]==s[j]) UPD(f[i][j],std::min(f[i+1][j-1]+1,std::min(f[i+1][j],f[i][j-1])));
else for(register int k=i;k<j;++k) UPD(f[i][j],f[i][k]+f[k+1][j]);
}
}
printf("%d\n",f[1][n]);
return 0;
}