bzoj1068:[SCOI2007]压缩
思路:区间dp,设状态f[l][r][bo]表示区间[l,r]的答案,bo=1表示该区间可以放M也可以不放M,bo=0表示该区间不能放M,并且对于任意一个状态f,l和l-1之间均有一个M,于是就可以进行转移了。
对于区间[l,r]中的任意位置都可能要放一个M,于是当bo=1时f[l][r][bo]=min(f[l][r][bo],f[l][mid][bo]+1(即M的长度,同时为了让每个状态f前均有一个M)+f[mid+1][r][bo])
同样也可以不放M,只考虑压缩前一段,即f[l][r][bo]=min(f[l][r][bo],f[l][mid][bo]+r-mid)
然后还剩下当前状态压缩的情况(这里有一个坑点,也怪我不看题,一个R压缩的是距离它第一个M到它全部的字符,即使它们之间有R,应该解压之后再压缩,换句话说就是压缩abababab不是abRRR而是abRR,被这里坑了好久还以为hack掉了std。。。。),即当前状态能被完全压缩当且仅当它是由2^x个相等的字串组成的,因此每次折半判断即可。
f[l][r][bo]=min(f[l][r][bo],f[l][(l+r)>>1][0]+1(1即R的长度)) 然后一定要判当前区间长度是否是偶数,而且必须由bo是0的状态转移过来,因为这段区间如果再放M,那压缩的就不再是前面一整段了。。。。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 55 8 #define inf 1e9 9 10 char s[maxn]; 11 int f[maxn][maxn][2]; 12 13 bool check(int l,int r,int len){ 14 int mid=(l+r)>>1; 15 for (int i=l;i<=mid;i++) 16 if (s[i]!=s[i+len]) return 0; 17 return 1; 18 } 19 20 int dp(int l,int r,int bo){ 21 if (l==r) return f[l][r][bo]=1/*+((bo==1)?inf:0)*/; 22 if (f[l][r][bo]) return f[l][r][bo]; 23 int n=r-l+1;f[l][r][bo]=r-l+1; 24 if (bo) for (int mid=l;mid<r;mid++) f[l][r][bo]=min(f[l][r][bo],dp(l,mid,bo)+1+dp(mid+1,r,bo)); 25 for (int mid=l;mid<r;mid++) f[l][r][bo]=min(f[l][r][bo],dp(l,mid,bo)+r-mid); 26 if (!(n&1) && check(l,r,n/2)) f[l][r][bo]=min(f[l][r][bo],dp(l,l+n/2-1,0)+1); 27 return f[l][r][bo]; 28 } 29 30 int main(){ 31 scanf("%s",s+1); 32 printf("%d\n",dp(1,strlen(s+1),1)); 33 return 0; 34 }