[BZOJ1068]-[SCOI2007]压缩-区间DP

说在前面

一开始自己yy了一个DP,感觉代码正确的不行,然而一直WA
后来发现理解错题意了…Emmmmm….


题目

BZOJ1068传送门
题面有点长…可以直接去BZOJ上看


解法

按照题意,主要是使用R在进行压缩,而压缩的起点是上一个M(也就是说,只要出现了M,压缩的起点就会被重置)
手动模拟压缩过程,可以发现大概是这样的:

  1. 如果当前串长度为偶数且对称,那么后半段可以用R来代替,而前半段不能包含M(因为M会重置起点)
  2. 字符串被分成两部分压缩,最后合起来。这种情况中间可能会加一个M来重置起点
  3. 字符串长度为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() ;
}
发布了268 篇原创文章 · 获赞 29 · 访问量 6万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览