HDU_3872 Dragon Ball dp

http://acm.hdu.edu.cn/showproblem.php?pid=3872

題意:

有N個球,每個球都有一個type和energy,現在要求將N個球分成若干組,每個組的中要求沒有和最右邊的球一樣type的球,每個組的得分是該組中所有球的最大值, 求所有組的最小得分。

思路:

這個題目dp的狀態很好表示,用Fi表示前i個球分成若干組之後的最小得分,狀態轉移方程就是:Fi = Fj + max{ j+1 ... i } ,其中 pre[i] <= j < i ,但是如果直接去枚舉j,複雜度就會達到O(N^2),肯定會超時。這就說明我們需要去優化這個dp的決策點的選擇,我們注意到對於一個固定的i,j的決策點是稀疏的,這樣我們就可以用一個單調隊列來維護energy 的單調遞減的性質,對於隊列中的任意兩個數的之間的決策點來說,max{j+1  .. i } 就是一定的,那麼我們只需要求出該區間內的最小dp值就可以了。這樣我們的思路就很清楚了,先處理出每個位置最左邊的決策位置pre[i], 然後二分找到pre[i]在單調隊列中的區間,對於這個區間右邊的區間都是最值可能存在的地方,於是我們可以用一棵線段樹來維護區間內dp的最小值,同時也維護單調隊列中位置中的最小值,這樣邊求解邊維護線段樹就可以在O(nlogn)的時間內求解本題。 


代碼:

#include <stdio.h>
#include <string.h>
typedef __int64 LL ;
const LL inf = 10000000000000000LL  ;
const int MAXN = 100010 ;
LL N ;
LL tt[MAXN], ee[MAXN] ;
LL pre[MAXN] , hash[MAXN] ;
LL dp[MAXN] ;
LL que[MAXN] , top ;
LL min1[MAXN<<2] , min2[MAXN<<2] ;

void init(){
    scanf("%I64d",&N);
    for(int i=1;i<=N;i++)   scanf("%I64d",&tt[i]) ;
    for(int i=1;i<=N;i++)   scanf("%I64d",&ee[i]) ;
    memset( hash , -1, sizeof(hash) );

    for(int i=1;i<=N;i++){
        if( hash[ tt[i] ] == -1 ) pre[i] = 0 ;
        else    pre[i] = hash[ tt[i] ] ;
        hash[ tt[i] ] = i ;
    }
}

LL find(LL l , LL r,  LL val ){
    while( l <  r ){
        LL mid = (l + r ) >> 1 ;
        if( que[mid] >= val )  r = mid ;
        else        l = mid + 1 ;
    }
    return l ;
}

void up2(int idx){
    LL ls = idx<<1 , rs = idx<<1|1 ;
    min2[idx] = min2[ls] > min2[rs] ? min2[rs] : min2[ls] ;
}

void update2(LL l , LL r, LL idx,  LL pos , LL val){
    if(l == r){
        min2[idx] = val ;
        return ;
    }
    LL mid = (l + r) >> 1 , ls = idx<<1 , rs = idx<<1|1 ;
    if( pos <= mid )    update2( l , mid , ls,  pos , val ) ;
    else                update2( mid+1,  r , rs , pos , val ) ;
    up2( idx );
}

void build(LL l , LL r, LL idx){
    min1[idx] = min2[idx] = inf ;
    LL mid = (l + r) >> 1 , ls = idx<<1 ,rs = idx<<1|1;
    if(l == r)  return ;
    build(l , mid , ls) ; build( mid+1, r, rs) ;
}

LL query1(LL l ,LL r, LL idx , LL a, LL b){
    if( l==a && r==b){
        return min1[idx] ;
    }
    LL mid = (l + r) >> 1 , ls = idx<<1 ,rs = idx<<1|1 ;
    if( b<=mid )    return query1( l , mid , ls , a ,b ) ;
    else if( mid < a )  return query1( mid+1 , r ,rs , a ,b ) ;
    else{
        LL aa = query1( l , mid , ls , a, mid) ;
        LL bb = query1( mid+1, r, rs, mid+1, b) ;
        return aa > bb ? bb : aa ;
    }
}

void up1(LL idx){
    LL ls = idx<<1 , rs = idx<<1|1 ;
    min1[idx] =  min1[ls] > min1[rs] ? min1[rs] : min1[ls] ;
}

void update1(LL l ,LL r, LL idx, LL pos , LL val){
    if(l == r){
        min1[idx] = val ; return ;
    }
    LL mid = (l + r) >> 1 ,ls = idx<<1 , rs = idx<<1|1 ;
    if( pos<=mid )  update1(l , mid, ls , pos, val );
    else            update1(mid+1 , r  , rs , pos , val) ;
    up1( idx ) ;
}

LL query2(LL l ,LL r, LL idx,  LL a, LL b){
    if(l==a && r==b){
        return min2[idx]  ;
    }
    LL mid = (l + r) >> 1 , ls = idx<<1 ,rs = idx<<1|1 ;
    if( b<=mid )    return query2( l , mid , ls , a , b) ;
    else if( mid<a )    return query2( mid+1, r , rs , a ,b );
    else{
        LL aa = query2(l  , mid , ls , a , mid ) ;
        LL bb = query2(mid+1, r, rs,  mid+1, b);
        return aa > bb ? bb : aa ;
    }
}

void solve(){
    LL a ,res1 ,res2 ;
    dp[0] =  0 ;
    dp[1] = ee[1] ; top = 0 ;
    que[ top++ ] = 1 ;
    build(0 , N , 1) ;
    update2( 0 , N , 1 , 0 , ee[1] ) ;
    update1( 0 , N , 1 , 1 , dp[1] ) ;
    update1( 0 , N , 1 , 0 , dp[0] ) ;

    for(LL i=2;i<=N;i++){
        while( top  ){
            a = que[top-1] ;
            if( ee[a] < ee[i] ){
                update2(0 , N , 1 , top-1 , inf ) ;
                top-- ;
            }
            else    break ;
        }
        if( top ){
            a = que[top-1] ;
            res1 = query1(0 , N , 1 , a, i-1 );
        }
        else
            res1 = 0 ;
        res1 = res1 + ee[i] ;
        que[top++] = i ;
        update2(0 , N , 1 , top-1 , res1 ) ;
        LL s = pre[i] + 1 , e = i ;
        LL ppp = find( 0 , top-1 , s ) ;

        res1 = query1(0 , N , 1 , pre[i] , que[ppp]-1) ;
        res1 += ee[ que[ppp] ] ;
        if( ppp+1<top ){
            res2 = query2( 0 , N , 1 , ppp+1 , top-1 ) ;
            res1 = res1 < res2 ? res1 : res2  ;
        }
        dp[i] = res1 ;
        update1( 0 , N  ,1  ,i , dp[i] ) ;
    }
    printf("%I64d\n",dp[N]);
}

int main(){
    int T;  scanf("%d",&T);
    while( T-- ){
        init() ;
        solve() ;
    }
    return 0 ;
}


發佈了211 篇原創文章 · 獲贊 24 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章