说在前面
一如既往的菜
只能切水题
题目
解法
对于某个和旁边相同高度的栅栏,我们不需要让它增加很高,只需要让它和旁边的栅栏高度不同就行了
显然一个栅栏的高度增加不会超过2,状压就行了
证明:如果最优方案中,有一个栅栏的高度增加了3,那么必然存在0,1,2中的某个值,将它的高度增加值换成该值后,仍然不与相邻栅栏冲突(因为相邻栅栏高度只有两种,而 Δ = 0 , 1 , 2 \Delta=0,1,2 Δ=0,1,2有三种高度)。所以原来的方案不是最优的,证毕
下面是代码
#include <cstdio>
#include <cstring>
#include <algorithm>
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() ;
}
}