bzoj1068: [SCOI2007]压缩
区间dp。
bool t代表区间内是否能含M。
如果不能含M的话有 res=min{f[l][i][0]+r-i}。(i<r) (串长的最小值等于前面串压缩后的最小值和不压缩后面串的长度)。
如果字符串长度为偶数,且前半串等于后半串,还有 f[l][r][t]=min(f[l][(l+r)>>1][t]+1) (后半串用1个R替代)。
如果t=1时,除上面俩个还有res=min{f[l][i][1]+1+f[i+1][r][1]}。
状态和3种状态转移方程比较难想。很大程度是因为对区间dp不熟悉。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 50 + 10; int f[maxn][maxn][2]; char s[100]; bool same(int l,int r) { int n=r-l+1; if(n%2) return 0; int mid=(l+r)>>1; for(int i=l,j=mid+1;i<=mid;i++,j++) if(s[i]!=s[j]) return 0; return 1; } int dp(int l,int r,bool t) { if(l==r) return 1; if(f[l][r][t]) return f[l][r][t]; int &res=f[l][r][t]; res=r-l+1; if(t) for(int i=l;i<r;i++) res=min(res,dp(l,i,1)+dp(i+1,r,1)+1); for(int i=l;i<r;i++) res=min(res,dp(l,i,t)+r-i); if(same(l,r)) res=min(res,dp(l,(l+r)>>1,0)+1); return res; } int main() { scanf("%s",s+1); printf("%d\n",dp(1,strlen(s+1),1)); return 0; }