1068. [SCOI2007]压缩【区间DP】
Description
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小
写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没
有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程
另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。
Input
输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。
Output
输出仅一行,即压缩后字符串的最短长度。
Sample Input
bcdcdcdcdxcdcdcdcd
Sample Output
12
HINT
在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR。
【限制】
100%的数据满足:1<=n<=50 100%的数据满足:1<=n<=50
艹二维做法艹了好久,竟然还过样例了还有60(惊)
果然还是数据太水了
正解的三维做法非常巧妙
dp[x][y][1\0]表示[x,y]区间内是否有M。默认x-1前面跟着一个M
枚举断点k
dp[x][y][1]就可以从k划分的两个区间的0\1状态转移了。毕竟中间有M了可以为所欲为(雾)
dp[x][y][0]就只能从0的转移过来了。注意如果[x,y]可以从中间一分为二相同的话,还要加一个状态转移
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 char a[150]; 6 int dp[150][150][2],n; 7 int check(int x,int y) 8 { 9 int mid=(x+y)/2; 10 for (int i=0;i<mid-x+1;++i) 11 if (a[x+i] != a[mid+i+1]) 12 return 0; 13 return 1; 14 } 15 16 int main() 17 { 18 scanf("%s",a+1); 19 n=strlen(a+1); 20 memset(dp,0x3f,sizeof(dp)); 21 for (int i=1; i<=n; ++i) 22 { 23 for (int j=1; j<=n-i+1; ++j) 24 { 25 int x=j,y=j+i-1; 26 dp[x][y][0]=dp[x][y][1]=y-x+1; 27 for (int k=x; k<y; ++k) 28 { 29 dp[x][y][1]=min(dp[x][y][1],min(dp[x][k][0],dp[x][k][1])+1+min(dp[k+1][y][0],dp[k+1][y][1])); 30 dp[x][y][0]=min(dp[x][y][0],dp[x][k][0]+y-k); 31 if ((y-x+1)%2==0 && check(x,y)) dp[x][y][0]=min(dp[x][y][0],dp[x][(x+y)/2][0]+1); 32 } 33 34 } 35 } 36 printf("%d",min(dp[1][n][0],dp[1][n][1])); 37 }