BZOJ1068 [SCOI2007]压缩 区间动态规划 字符串
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ1068
题目概括
(其实是复制的)
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程。
另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。
题解
这又是一道字符串的题目。
记得有人说过——字符串的题目就是哈希+乱搞????
言归正传:
一看就是一道区间动归。
所以我们用:
g[i][j]表示禁止更新缓冲,但是允许解压R的情况 (最短值)
f[i][j]表示允许更新缓冲,但是禁止解压R的情况 (最短值)
于是只需要写两个记忆化dfs就可以了。
具体操作看代码。
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; const int N=50+5; char str[N]; int n,g[N][N],f[N][N]; // g[i][j]表示禁止更新缓冲,但是允许解压R的情况 // f[i][j]表示允许更新缓冲,但是禁止解压R的情况 bool can_fold[N][N]; bool fold_check(int L,int R){ int len=R-L+1; if (len&1) return 0; int M=L+len/2; for (int i=0;i<len/2;i++) if (str[L+i]!=str[M+i]) return 0; return 1; } int G(int L,int R){ if (L>R) return 0; if (g[L][R]!=-1) return g[L][R]; g[L][R]=R-L+1; for (int i=1;L+i*2-1<=R;i++){ int M=L+i*2-1; if (can_fold[L][M]) g[L][R]=min(g[L][R],G(L,L+i-1)+1+(R-M)); } return g[L][R]; } int F(int L,int R){ if (L>R) return 0; if (f[L][R]!=-1) return f[L][R]; f[L][R]=G(L,R); for (int i=L;i<=R;i++) f[L][R]=min(f[L][R],min(G(L,i)+1+F(i+1,R),G(L,i)+(R-i))); return f[L][R]; } int main(){ scanf("%s",str+1); n=strlen(str+1); memset(can_fold,0,sizeof can_fold); for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) can_fold[i][j]=fold_check(i,j); memset(f,-1,sizeof f); memset(g,-1,sizeof g); for (int i=1;i<=n;i++) f[i][i]=g[i][i]=1; printf("%d",F(1,n)); return 0; }