首页 > 代码库 > 【BZOJ-1068】压缩 区间DP

【BZOJ-1068】压缩 区间DP

1068: [SCOI2007]压缩

Time Limit: 1 Sec  Memory Limit: 128 MB
Submit: 1001  Solved: 615
[Submit][Status][Discuss]

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

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