首页 > 代码库 > 【BZOJ-1068】压缩 区间DP
【BZOJ-1068】压缩 区间DP
1068: [SCOI2007]压缩
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 1001 Solved: 615
[Submit][Status][Discuss]
Description
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程
另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。
Input
输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。
Output
输出仅一行,即压缩后字符串的最短长度。
Sample Input
Sample Output
HINT
在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR。
【限制】
100%的数据满足:1<=n<=50 100%的数据满足:1<=n<=50
Source
Solution
区间DP,做的太少了,以至于这道题转移设计出现一点错误,其实这道题写记搜比递推更容易理解.
状态很好想到$f[l][r][0/1]$表示区间$[l,r]$中有M/无M的最短,这样显然答案为$min(f[1][n][0],f[1][n][1])$
转移的时候显然是要枚举断点的, 断点为k,转移:
$f[i][j][1]=min(f[i][j][1],min(f[i][k][0],f[i][k][1])+min(f[k+1][j][0],f[k+1][j][1])+1);$
$f[i][j][0]=min(f[i][j][0],f[i][k][0]+j-k);$
然后如果枚举到的区间$[l,r]$,如果这个$[l,r]$可以缩成一个,那么就缩,所以得到$f[i][j][0]=f[i][(i+j)>>1][0]+1$
然后就可以了
Code
#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<cstring>using namespace std;#define MAXN 100 char S[MAXN];int len,f[MAXN][MAXN][2];bool check(int l,int r){ int mid=(l+r)>>1; if ((r-l+1)&1) return 0; for (int i=1; i<=mid-l+1; i++) if (S[l+i-1]!=S[mid+i]) return 0; return 1;}int main(){ scanf("%s",S+1); len=strlen(S+1); for (int i=len; i>=1; i--) for (int j=i; j<=len; j++) { f[i][j][0]=f[i][j][1]=j-i+1; for (int k=i; k<j; k++) f[i][j][1]=min(f[i][j][1],min(f[i][k][0],f[i][k][1])+min(f[k+1][j][0],f[k+1][j][1])+1); for (int k=i; k<j; k++) f[i][j][0]=min(f[i][j][0],f[i][k][0]+j-k); if (check(i,j)) f[i][j][0]=f[i][(i+j)>>1][0]+1; if (j-i+1==1) f[i][j][1]=len+1; } printf("%d\n",min(f[1][len][1],f[1][len][0])); return 0;}
这道题搞了一会,感觉有点zz
【BZOJ-1068】压缩 区间DP