UESTC_1558 Charitable Exchange 弦断树优化dp

http://acm.uestc.edu.cn/problem.php?pid=1558

题意:

有N中交换的类型,每种交换都可以表示为(a ,b, c )表示的意思是用a元钱的东西,花上c的时间就可以将其变成价值为b的东西,数据保证a <= b ,初始时你有一件1元的东西,问你要使得最后的价值最起码是M,所要用的最少的时间是的多少。

思路:
这个题目如果数据量比较小的话是可以考虑用最短路做的,用最短路的话建图就需要O(N^2)的复杂度,所以这样是肯定不行的。我们考虑用dp来求。首先将所有的价值离散化,这样就变成了2*N个点,然后可以发现我们能获得的价值数一定是在某个交易的b ,这样我们就可以用dp[i]表示有i元的最少耗时(这里的i是离散化之后的i),这样dp的转移方程就是: dp[i] = min( dp[k] ) + exchange(j) ,其中exchange(j)的b等于i ,并且 exchange(j).a <= k <= exchange(j).b , 这样我们就可以发现,每次找决策点k的时候,就是在(a,b)内找一个dp的最小值,这样我们就可以用线段树在O(logn)的时间内找到该最小值,另外为了保持后效性,我们可以将每个exchange按照b的大小进行一次排序, 这样就可以顺序进行dp了。 

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>

typedef long long LL ;
const int MAXN = 100010 ;
int N , M ;
LL T[MAXN<<1] ;
const LL INF = 1e16  ;

struct Point{
    int s, e,t ;
    Point(){}
    Point(int a, int b ,int c)
    :s(a) , e(b) , t(c) {}
    bool operator<( const Point& cmp ) const {
        return e < cmp.e ;
    }
}p[MAXN] ;

LL min[MAXN<<3] ;
int cnt ;

inline LL MIN(LL a, LL b){
    return a > b ? b : a ;
}

void update(int l ,int r, int idx, int pos, LL val ){
    if(l == r){
        min[idx] = MIN( min[idx] , val );
        return ;
    }
    int mid = (l + r) >> 1 ;
    int ls = idx<<1 , rs = idx<<1|1 ;
    if( pos<=mid )  update(l , mid ,ls , pos , val );
    else            update(mid+1, r, rs , pos, val );
    min[idx] = MIN( min[ls] , min[rs] );
}

int find(int v, int l ,int r){
    while( l<r ){
        int mid = (l + r) >> 1 ;
        if( T[mid] < v )    l = mid + 1 ;
        else    r = mid ;
    }
    return l ;
}

LL query(int l, int r, int idx, int a, int b){
    if(l==a && r==b){
        return min[idx] ;
    }
    int mid = (l + r) >> 1 ;
    int ls = idx<<1 ,rs = idx<<1|1 ;
    if( b<=mid )    return query(l , mid , ls , a, b) ;
    else if( mid<a )    return query(mid+1, r, rs, a ,b );
    else{
        return MIN( query(l ,mid, ls , a ,mid )  , query(mid+1, r ,rs , mid+1, b) );
    }
}

void build(int l , int r, int idx){
    min[idx] = INF ;
    if(l == r) {
        return ;
    }
    int mid = (l + r) >> 1 ;
    int ls = idx<<1 , rs = idx<<1|1 ;
    build(l , mid ,ls ) ;
    build(mid+1, r, rs) ;
}

void solve(int n){
    build(0,  n ,1 );
    update(0 , n ,1 ,0 , 0);
    for(int i=0;i<N;i++){
        int s = p[i].s  ;
        int e = p[i].e ;
        LL tt = p[i].t ;
        LL _min = query(0 , n ,1 , s , e );
        if( _min == INF )   continue ;
        update(0 , n ,1 , e , _min+tt ) ;
    }
    int  pos = find(M , 0 , n );
    LL ans = query(0 , n , 1 ,pos, n ) ;
    if( ans == INF )    printf("-1\n");
    else    printf("%lld\n",ans);
}

int main(){
    int TT , a, b, c ;
    scanf("%d",&TT);
    for(int cas=1;cas<=TT;cas++){
        scanf("%d %d",&N,&M);
        cnt = 0 ;
        for(int i=0;i<N;i++){
            scanf("%d %d %d",&a,&b,&c);
            p[i] = Point(b ,a ,c );
            T[cnt++] = b ; T[cnt++] = a ;
        }
        T[cnt++] = M ;
        T[cnt++] = 1 ;      //一开始NC地没加这个点,WA了N次。。。
        std::sort(T, T+cnt ) ;
        int n = 0 ;
        for(int i=1;i<cnt;i++){
            if( T[i]!=T[i-1] )  T[++n] = T[i] ;
        }
        for(int i=0;i<N;i++){
            p[i].s = find( p[i].s , 0 , n ) ;
            p[i].e = find( p[i].e , 0 , n ) ;
        }
        std::sort(p ,p + N) ;
        printf("Case #%d: ",cas);
        solve(n);
    }
    return 0;
}


你可能感兴趣的:(UESTC_1558 Charitable Exchange 弦断树优化dp)