[SCOI2007]压缩
一道挺好的区间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;
}
一如既往,万事胜意