bzoj1068(区间dp,字符串压缩)

这类题真心不会,想思路直接呵呵。。


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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值