假设你有一条长度为 55 的木版,初始时没有涂过任何颜色。你希望把它的 5 5 5个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 5 5 5的字符串表示这个目标:RGBGR。
每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成 RRRRR,第二次涂成 RGGGR,第三次涂成 RGBGR,达到目标。
用尽量少的涂色次数达到目标。
输入仅一行,包含一个长度为 n n n的字符串,即涂色目标。字符串中的每个字符都是一个大写字母,不同的字母代表不同颜色,相同的字母代表相同颜色。
仅一行,包含一个数,即最少的涂色次数。
40 % 40\% 40% 的数据满足 1 ≤ n ≤ 10 1\le n\le10 1≤n≤10。
100 % 100\% 100% 的数据满足 1 ≤ n ≤ 50 1\le n\le 50 1≤n≤50。
我们把问题抽象成为一个长度为 n n n的字符串 s s s,字符串中每种字母代表一种颜色。 用 d p [ x ] [ y ] dp[x][y] dp[x][y]表示让区间 [ x , y ] [x, y] [x,y] 满足条件的最小次数。然后我们按照区间长度从小到大枚举每一个区间。
我们可以先把 [ x , y ] [x, y] [x,y]整体涂成一个颜色,然后再去处理 [ x + 1 , y − 1 ] [x + 1, y - 1] [x+1,y−1]。对应的转移为:
dp[x][y]=min(dp[x][y],dp[x+1][y−1]+1)或者对于区间 [ x , y − 1 ] [x, y - 1] [x,y−1],因为 x x x是最边上的位置,所以最优情况下,一定可以第一个涂 x x x位置,这样我们可以让这次涂色延长到 y y y,这样实际上 y y y就顺带满足条件了,区间 [ x + 1 , y ] [x + 1, y] [x+1,y]也是同样原理,对应的转移为:
dp[x][y]=min(dp[x][y−1],dp[x+1][y])那么实际上 x x x和 y y y没有关系,所以我们可以把区间分成两部分来完成,一部分包含 x x x,一部分包含 y y y。对应的转移为:
∀ x≤k<y,dp[x][y]=min(dp[x][y],dp[x][k]+dp[k+1][y])按照刚才的分析,可以得到如下代码:
#include <iostream> #include <string> #include <algorithm> #include <cstring> using namespace std; const int inf = 0x3f3f3f3f; int dp[55][55]; int main(){ string s; cin>>s; memset(dp,0x3f,sizeof(dp)); for(int i=0;i<s.size();i++){ dp[i][i]=1; } for(int l=2;l<=s.size();l++){ for(int i=0;i<s.size()-l+1;i++){ int j=i+l-1; if(s[i]==s[j]){ if(l==2){ dp[i][j]=1; } else{ dp[i][j]=min(min(dp[i+1][j],dp[i][j-1]),dp[i+1][j-1]+1); } }else{ for(int k=i;k<j;k++){ dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); } } } } cout<<dp[0][s.size()-1]<<endl; return 0; }