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 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值