1. 程式人生 > >[BZOJ1150]-[CTSC2007]資料備份Backup-dp凸優化

[BZOJ1150]-[CTSC2007]資料備份Backup-dp凸優化

說在前面

凸優化真好用

然而二分上下界me從來都沒有一次性對過
撒幣DP都能寫錯,各種細節錯
me怕是廢了


題目

洛谷P3620傳送門
BZOJ1150傳送門
看題可戳傳送門


解法

首先兩維的DP是很好想的
然後yy一下,或者打個表,發現這個dp是凸的(證明不會
然後寫個凸優化,發現A了


下面是程式碼

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , K , a[100005
] , MX ; struct Data{ long long fee ; int k ; Data( const long long _ = 0 , const int __ = 0 ):fee(_) , k(__){} ; bool operator < ( const Data &A ) const { return fee < A.fee || ( fee == A.fee && k < A.k ) ; } } f[100005] ; Data check( long long delta ){ Data best = Data( 0
, 0 ) ; for( int i = 2 ; i <= N ; i ++ ){ f[i] = Data( 0 , 0 ) ; if( delta + a[i] < 0 ) f[i] = Data( best.fee + a[i] + delta , best.k + 1 ) ; best = min( best , f[i-1] ) ; f[i] = min( f[i] , best ) ; } return f[N] ; } void solve(){ long long
lf = -MX , rg = 0 , ans = 0 ; while( lf <= rg ){ long long mid = ( lf + rg ) / 2 ; Data res = check( mid ) ; if( res.k == K ) printf( "%lld\n" , res.fee - 1ll * K * mid ) , exit( 0 ) ; if( res.k > K ) lf = mid + 1 ; else rg = mid - 1 , ans = mid ; } Data res = check( ans ) ; printf( "%lld\n" , res.fee - 1ll * K * ans ) ; } int main(){ scanf( "%d%d" , &N , &K ) ; for( int i = 1 ; i <= N ; i ++ ) scanf( "%d" , &a[i] ) ; MX = a[N] ; for( int i = N ; i ; i -- ) a[i] -= a[i-1] ; solve() ; }