[SCOI2007]压缩

link

一道挺好的区间DP,除了数据比较弱怎么乱搞都能拿分(比如有个数据是xyz)。主要难点是搞清楚压缩的本质,搞清楚了之后就简单了。

abab压缩成abR可以理解,但考场上的我看见ababcababc压缩成abRcR时就感觉大事不妙了。前者可以简单的认为是一个区间内元素的复制,但后面的情况发现元素竟然还会变化,再加上题目中缓冲区的干扰使得我对这道题放弃抵抗,甚至到交卷时还没有读懂题目,就很悲催。

其实没必要理解得那么复杂。可以先展开成MabRabcRababc。发现了什么,每个R就相当于是把它和最近的M之间的字符重复了一遍。这样就简单了。可以想到每两个M就是两个小段,段和段之间是互不干扰的。既然如此就可以区间DP了。就像上面的情况,ababcababc可以认为是ababc折叠之后再加上一个R,这样问题就可以继续下去了。但有个点需要注意就是上面的ababc中是不可以再次出现M的,因为对于串ababcababc就不能把ababc压缩成MabRc,这个例子是没有问题但总有例子有问题。既然如此就考虑给DP加半维来维护这个区间内是否只有一个M。只有一个M的情况只能从只有一个M的情况转移,但多个M的情况可以从两种情况同时转移过来,做好记忆化即可。

代码比较简洁。

#include<bits/stdc++.h>
//#define feyn
using namespace std;
const int N=60;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}
inline void check(int &s1,int s2){
	if(s1>s2)s1=s2;return;
}

int m,g[N][N][2];
char a[N];
inline int f(int l,int r,int op){
	
	if(g[l][r][op])return g[l][r][op];
	if(l==r)return g[l][r][op]=2;
	g[l][r][op]=N;
	if(op==0){
		if((r-l+1)%2==0){
			int mid=l+r>>1;bool ok=true;
			for(int i=0;l+i<=mid;i++){
				if(a[l+i]!=a[mid+1+i]){ok=false;continue;}
			}
			if(ok)check(g[l][r][op],f(l,mid,0)+1);
		}
		for(int i=l;i<r;i++)check(g[l][r][op],f(l,i,0)+r-i);
	}
	else{
		for(int i=l;i<r;i++){
			check(g[l][r][op],min(f(l,i,0),f(l,i,1))+min(f(i+1,r,0),f(i+1,r,1)));
		}
	}
	return g[l][r][op];
}

signed main(){
	
	#ifdef feyn
	freopen("in.txt","r",stdin);
	#endif
	
	scanf("%s",a+1);
	m=strlen(a+1);
	printf("%d\n",min(f(1,m,1),f(1,m,0))-1);
	
	return 0;
}
posted @ 2022-07-08 21:00  Feyn618  阅读(27)  评论(0编辑  收藏  举报