这类题真心不会,想思路直接呵呵。。
f[l][r][k]表示i-j这段区间,是否放置M(k=1/0)压缩到的最短长度。我们标记此时l-1一定是有M的,或者l-1=0。就是,在这段区间l~r放R的话,一定是从l开头到R的位置和R后面的位置相同,其实就是保证了,l到R之间没有M
对于k=1,及对于在这个区间放m,我们分四种情况:
首先枚举M的位置i,然后对于被M分割的两个区间l到i,i+1到r:在两个区间中间放M,因为要保证每个区间l-1是M,再能保证性质
k可以是
1,1
1,0
0,1
0,0
对于k=0,分两种情况:
如果此时左右两半相同,那么右边可以用R代替;
递归处理左边最多能压缩到什么,注意此时递归t要等于0,因为左部分不能放M
另外,有一些是不被压缩的,枚举循环结束的位置i,i+1到r不做处理。
答案就是min(f[1][l][1],f[1][l][0])
为什么这就是对的,为什么这么想?(这题要好好记录一下)
首先,对于一个区间来说,我们要把它压缩,有哪些方案,决策?
最简单的,如果这个区间左右两半是相同的,就直接把它压缩就好。//第三种操作
另一种情况,还是这个l~r的区间,如果左边的l~k是左右两边对称的,后边的就不构成对称了,那么两种情况
右边也能自成一个对称--》中间加一个M,处理右边//第一种操作
右边不能成对称了--》直接把右边的长度加上//第二中操作
这只是一些情况,还有很多,通用来说的话:
就是1:压缩前面和后面
2:只压缩前面
3:整个压缩
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
int f[60][60][2];
int n;
char s[60];
bool same(int l,int r)
{
int len=(r-l+1);
if (len&1) return false;
len>>=1;
int tmp=l+len;
for (int i=1;i<=len;i++) if (s[i+l-1]!=s[i+tmp-1]) return false;
return true;
}
int dfs(int l,int r,int t)
{
if (l==r) return 1;
if (f[l][r][t]!=-1) return f[l][r][t];
int ans=r-l+1;
if (t==1)
for (int i=l;i<r;i++)
ans=min(ans,1+min(dfs(l,i,0),dfs(l,i,1))+min(dfs(i+1,r,0),dfs(i+1,r,1)));
for (int i=l;i<r;i++) ans=min(ans,dfs(l,i,t)+r-i);
if (t==0&&same(l,r))
{
int mid=(l+r)>>1;
ans=min(ans,dfs(l,mid,0)+1);
}
f[l][r][t]=ans;
return ans;
}
int main()
{
scanf("%s",s+1);
n=strlen(s+1);
memset(f,-1,sizeof(f));
printf("%d",min(dfs(1,n,0),dfs(1,n,1)));
return 0;
}