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 }
View Code
posted @ 2016-10-10 20:29  DUXT  阅读(...)  评论(...编辑  收藏