说在前面
一开始自己yy了一个DP,感觉代码正确的不行,然而一直WA
后来发现理解错题意了…Emmmmm….
题目
BZOJ1068传送门
题面有点长…可以直接去BZOJ上看
解法
按照题意,主要是使用R在进行压缩,而压缩的起点是上一个M(也就是说,只要出现了M,压缩的起点就会被重置)
手动模拟压缩过程,可以发现大概是这样的:
- 如果当前串长度为偶数且对称,那么后半段可以用R来代替,而前半段不能包含M(因为M会重置起点)
- 字符串被分成两部分压缩,最后合起来。这种情况中间可能会加一个M来重置起点
- 字符串长度为1,无法压缩
于是大概可以明确这是一个区间DP,并且DP的时候需要加限制「允不允许包含M」
那么定义dp[lf][rg][0/1]表示lf到rg这段字符串,是否允许包含M 的最小压缩长度
转移有两种:
1. 折半压缩加R,由dp[lf][mid][0]+1更新
2. 分成两部分压缩,前后是独立的,因此中间需要加一个M,也就是从dp[lf][k][1]+1+dp[k+1][rg][1]更新
(3.) 然而对于第二种情况,如果后面不压缩的话,中间加的M就多余了,于是为了补充第二种的不足,再加一个转移,考虑只压缩前面的情况,从dp[lf][k][admit_M]+rg-k更新。
下面是自带大常数的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , dp[2][55][55] ;
char ss[60] ;
bool same[60][30] ;
void init(){
N = strlen( ss + 1 ) ;
for( int k = 1 ; k <= ( N >> 1 ) ; k ++ )
for( int i = 1 ; i + k + k - 1 <= N ; i ++ ){
same[i][k] = true ;
for( int x = 0 ; x < k ; x ++ )
if( ss[ i+x ] != ss[ i+k+x ] ){
same[i][k] = false ; break ;
}
}
}
int dfs( bool add_M , int lf , int rg ){
if( rg == lf ) return 1 ;
if( dp[add_M][lf][rg] ) return dp[add_M][lf][rg] ;
int rt = 0x3f3f3f3f , len = rg - lf + 1 ;
if( add_M )
for( int i = lf ; i < rg ; i ++ )
rt = min( rt , dfs( true , lf , i ) + 1 + dfs( true , i + 1 , rg ) ) ;
for( int i = lf ; i < rg ; i ++ )
rt = min( rt , dfs( add_M , lf , i ) + rg - i ) ;
if( !( len&1 ) && same[lf][len>>1] )
rt = min( rt , dfs( false , lf , lf + len / 2 - 1 ) + 1 ) ;
return dp[add_M][lf][rg] = rt ;
}//zababcababc
void solve(){
printf( "%d" , dfs( true , 1 , N ) ) ;
}
int main(){
scanf( "%s" , ss + 1 ) ;
init() ;
solve() ;
}