BZOJ-1010: [HNOI2008]玩具装箱toy(DP斜率优化)

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1010

方程:f( i ) = min( cost( 1 , i ) , f( j - 1 ) + cost( j , i ) ) ( cost( i , j ) 表示从i个玩具到j个连续放入的花费 )

然后推导出斜率:

K( i , j ) = ( Y( i ) - Y( j ) ) / ( X( i ) - X( j ) )

X( i ) = s[ i - 1 ] + i Y( i ) = f[ i - 1 ] + X( i ) ^ 2

当 j > k 且 K( j , k ) < 2 ( s[ i ] + i - L ) 时 , 状态j优于k,然后单调队列维护斜率。

代码:

9345d688d43f8794f8eb0f56d01b0ef41bd53ab5.jpg.png
#include 

#include 

#include 

 

using namespace std ;

 

#define MAXN 50010

#define ll long long

 

int n ;

ll f[ MAXN ] , s[ MAXN ] , l ;

 

ll X( int j ) {

   return s[ j - 1 ] + j ;

}

 

ll Y( int j ) {

   return f[ j - 1 ] + X( j ) * X( j ) ;

}

 

double K( int i , int j  ) {

       ll x = X( i ) - X( j ) , y = Y( i ) - Y( j ) ;

       double k = double( y ) / double( x ) ;

       return k ;

}

 

ll cost( int i , int j ) {

   ll  ret = ( s[ j ] - s[ i - 1 ] + j - i - l ) ;

   return ret * ret ;

}

 

int Q[ MAXN ] , head = 1 , tail = 0 ;

 

int main(  ) {

    scanf( "%d%lld" , &n , &l ) ;

    s[ 0 ] = 0 ;

    for ( int i = 0 ; i ++ < n ; ) {

        ll c ; scanf( "%lld" , &c ) ;

        s[ i ] = s[ i - 1 ] + c ;

    }

    for ( int i = 0 ; i ++ < n ; ) {

        f[ i ] = cost( 1 , i ) ; ll A = s[ i ] + i - l ;

        while ( head < tail && K( Q[ head ] , Q[ head + 1 ] ) <= 2 * double( A ) ) head ++ ;

        if ( head <= tail ) f[ i ] = min( f[ i ] , f[ Q[ head ] - 1 ] + cost( Q[ head ] , i ) ) ;

        while ( head < tail && K( Q[ tail - 1 ] , Q[ tail ] ) >= K( Q[ tail ] , i + 1 ) ) tail -- ;

        Q[ ++ tail ] = i + 1 ;

    }

    printf( "%lld\n" , f[ n ] ) ;

    return 0 ;

}


你可能感兴趣的:(BZOJ-1010: [HNOI2008]玩具装箱toy(DP斜率优化))