• 【题解】Matryoshka World Finals 2013 区间DP


    三年前我觉得巨难的题今天被我秒了。。
    首先容易发现,最终的每个套娃组一定对应初始序列的一个连续区间
    这个连续区间还要求值域连续,并且最小值等于1
    对于每个这种“最后可以合并成一个套娃”的区间,可以用一个区间DP算出合并这些套娃的最小代价
    有很多东西需要预处理

    #include <bits/stdc++.h>
    
    using namespace std;
    const int N = 510;
    const int INF = 0x3f3f3f3f;
    int _w;
    
    int n, a[N], fuck[N][N];
    
    int diff[N][N], cons[N][N], cnt[N], minv[N][N];
    
    void prelude() {
    	for( int l = 1; l <= n; ++l )
    		for( int r = l; r <= n; ++r ) {
    			for( int i = l; i <= r; ++i )
    				++cnt[a[i]];
    			
    			diff[l][r] = true;
    			for( int i = 1; i <= 500; ++i )
    				if( cnt[i] >= 2 )
    					diff[l][r] = false;
    			
    			minv[l][r] = a[l];
    			for( int i = l; i <= r; ++i )
    				minv[l][r] = min( minv[l][r], a[i] );
    			
    			cons[l][r] = 1;
    			while( cnt[minv[l][r] + cons[l][r]] )
    				++cons[l][r];
    			
    			for( int i = l; i <= r; ++i )
    				--cnt[a[i]];
    		}
    	for( int i = 1; i <= n; ++i )
    		fuck[i][a[i]] = 1;
    	for( int i = 1; i <= n; ++i )
    		for( int j = 1; j <= 500; ++j )
    			fuck[i][j] += fuck[i-1][j];
    	for( int i = 1; i <= n; ++i )
    		for( int j = 1; j <= 500; ++j )
    			fuck[i][j] += fuck[i][j-1];
    }
    
    int dp[N][N];
    
    int calc( int l1, int r1, int l2, int r2 ) {
    	int tot_cnt = r2 - l2 + 1 + r1 - l1 + 1;
    	if( minv[l1][r1] < minv[l2][r2] ) {
    		int tmp = minv[l2][r2];
    		tot_cnt -= fuck[r1][tmp] - fuck[l1-1][tmp];
    	} else {
    		int tmp = minv[l1][r1];
    		tot_cnt -= fuck[r2][tmp] - fuck[l2-1][tmp];
    	}
    	return tot_cnt;
    }
    
    int DP( int l, int r ) {
    	int &now = dp[l][r];
    	if( now != -1 ) return now;
    	if( l == r ) return int(now = 0);
    	now = INF;
    	for( int mid = l; mid < r; ++mid ) {
    		now = min( now, DP(l, mid) + DP(mid+1, r) + calc(l, mid, mid+1, r) );
    	}
    	return now;
    }
    
    int f[N];
    
    void solve() {
    	memset(f, 0x3f, sizeof f);
    	memset(dp, -1, sizeof dp);
    	f[0] = 0;
    	for( int i = 1; i <= n; ++i ) {
    		for( int j = 1; j <= i; ++j ) {
    			if( diff[j][i] && minv[j][i] == 1 && cons[j][i] == (i-j+1) ) {
    				f[i] = min( f[i], f[j-1] + DP(j, i) );
    				// printf( "f[%d] = %d
    ", i, f[i] );
    			}
    		}
    	}
    	if( f[n] == INF ) puts("impossible");
    	else printf( "%d
    ", f[n] );
    }
    
    int main() {
    	_w = scanf( "%d", &n );
    	for( int i = 1; i <= n; ++i )
    		_w = scanf( "%d", a+i );
    	prelude();
    	solve();
    	return 0;
    }
    
  • 相关阅读:
    JSON 数字排序 多字段排序
    hdu-4747-Mex
    Android SDK更新缓慢或无法更新的解决方法
    UVA 11806
    SDL显示内存中的图像
    Cocos2d-x 2.1.5 简单动画
    codeforce343A
    android 控件的移动
    Erlang千万级用户游戏框架(Openpoker)源码文件分析清单
    八大排序c++可运行精简版,一目了然
  • 原文地址:https://www.cnblogs.com/mlystdcall/p/12497810.html
Copyright © 2020-2023  润新知