[Codeforces 1221D]Make The Fence Great Again-dp

说在前面

一如既往的菜
只能切水题


题目

codeforces 1227D传送门


解法

对于某个和旁边相同高度的栅栏,我们不需要让它增加很高,只需要让它和旁边的栅栏高度不同就行了
显然一个栅栏的高度增加不会超过2,状压就行了

证明:如果最优方案中,有一个栅栏的高度增加了3,那么必然存在0,1,2中的某个值,将它的高度增加值换成该值后,仍然不与相邻栅栏冲突(因为相邻栅栏高度只有两种,而 Δ = 0 , 1 , 2 \Delta=0,1,2 Δ=0,1,2有三种高度)。所以原来的方案不是最优的,证毕


下面是代码

#include 
#include 
#include 
using namespace std ;

int Q , N , a[300005] , b[300005] ;
long long f[2][27] ;

/*
0  000
1  001
2  010
3  011
4  100
5  101
6  110
7  111
*/

template <typename T>
void smin( T &A , T B ){
	if( A > B ) A = B ;
}

bool state_check( int pos , int s ){
	if( pos == 1 ) return true ;
	int s1 = s % 3 , s2 = ( s / 3 ) % 3 , s3 = ( s / 9 ) % 3 ;

	if( a[pos] + s1 != a[pos-1] + s2 && a[pos-2] + s3 != a[pos-1] + s2 ) return true ;  
	return false ;
}

bool trans_check( int t , int s ){
	int t1 = t % 3 , t2 = ( t / 3 ) % 3 ;
	int s2 = ( s / 3 ) % 3 , s3 = ( s / 9 ) % 3 ;
	if( t1 != s2 || t2 != s3 ) return false ;
	return true ;
}

void solve(){
	memset( f , 0 , sizeof( f ) ) ;
	int pre = 0 , now = 1 ;
	for( int i = 1 ; i <= N ; i ++ ){
		swap( now , pre ) ;
		for( int j = 0 ; j <= 26 ; j ++ ){
			f[now][j] = 1e18 ;
			if( state_check( i , j ) == false ) continue ;
			int delta = ( j % 3 ) * b[i] ;
			for( int k = 0 ; k <= 26 ; k ++ ){
				if( trans_check( k , j ) ) smin( f[now][j] , f[pre][k] + delta ) ;
			}
		}
	} 

	long long ans = 1e18 ;
	for( int i = 0 ; i <= 26 ; i ++ )
		smin( ans , f[now][i] ) ;
	printf( "%lld\n" , ans ) ;
}


int main(){
	scanf( "%d" , &Q ) ;
	while( Q -- ){
		scanf( "%d" , &N ) ;
		for( int i = 1 ; i <= N ; i ++ )
			scanf( "%d%d" , &a[i] , &b[i] ) ;
		a[0] = -1 ;
		solve() ;
	}
}

你可能感兴趣的:(------dp------,状压DP)