【HDU】5932.Backpack on Tree 【贪心+背包】

7 篇文章 0 订阅
本文介绍了一种解决背包树问题的方法,通过贪心算法与动态规划相结合的方式,在树形结构中寻找最优解。针对每个节点,将物品按价值密度排序,并进行贪心选择,之后利用动态规划调整解的空间分布,确保在不超过容量限制的情况下获得最大价值。
摘要由CSDN通过智能技术生成

题目链接:Backpack on Tree

每层,对价值除以花费的斜率从大到小排序,然后贪心到恰好大于询问的容量t,接下来开始dp。
设dp[i][j]表示贪心到第i个物品,取出里面花费为j的最小价值。dp2[i][j]表示斜率从小到大(即从右到左)取出花费为j的最大代价。
假设现在贪心得到的价值为t2(因为最大花费为5,所以t2-t<5),然后我枚举从贪心的解里面丢掉花费为x的最小价值dp[i][x],然后从没有加入贪心的物品里取出最大价值的dp2[i+1][x+t-t2],然后更新下最优值即可。因为lcm(1,2,3,4,5)=60,所以枚举的阈值设为70应该就没问题了。

#include <bits/stdc++.h>
using namespace std ;

typedef long long LL ;
typedef pair < int , int > pii ;

#define clr( a , x ) memset ( a , x , sizeof a )

const int MAXN = 20005 ;
const int MAXQ = 200005 ;
const int LIM = 70 ;

struct Node {
    int c , v ;
    bool operator < ( const Node& a ) const {
        return 1LL * v * a.c > 1LL * a.v * c ;
    }
} ;

Node a[MAXN] , b[MAXN] ;
vector < int > G[MAXN] ;
vector < pii > Q[MAXN] ;
int cost[MAXN] , val[MAXN] ;
LL dp[MAXN][LIM] , dp2[MAXN][LIM] ;
LL ans[MAXQ] ;
int pre[MAXN] ;
int sumc[MAXN] ;
LL sumv[MAXN] ;
int n , q ;
int cnt ;

void pre_dfs ( int u , int f ) {
    for ( int i = 0 ; i < G[u].size () ; ++ i ) {
        int v = G[u][i] ;
        if ( v == f ) continue ;
        pre[v] = u ;
        pre_dfs ( v , u ) ;
    }
}

void dfs ( int u , int f ) {
    b[++ cnt] = a[u] ;
    for ( int i = 0 ; i < G[u].size () ; ++ i ) {
        int v = G[u][i] ;
        if ( v == pre[u] ) continue ;
        dfs ( v , u ) ;
    }
}

int F ( int x , int l , int r ) {
    while ( l < r ) {
        int m = l + r >> 1 ;
        if ( sumc[m] >= x ) r = m ;
        else l = m + 1 ;
    }
    return l ;
}
long long inf=1LL<<60;
void solve () {
    scanf ( "%d" , &n ) ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        G[i].clear () ;
        Q[i].clear () ;
    }
    for ( int i = 1 ; i < n ; ++ i ) {
        int u , v ;
        scanf ( "%d%d" , &u , &v ) ;
        G[u].push_back ( v ) ;
        G[v].push_back ( u ) ;
    }
    for ( int i = 1 ; i <= n ; ++ i ) {
        scanf ( "%d%d" , &a[i].c , &a[i].v ) ;
    }
    scanf ( "%d" , &q ) ;
    for ( int i = 1 ; i <= q ; ++ i ) {
        int s , t ;
        scanf ( "%d%d" , &s , &t ) ;
        Q[s].push_back ( pii ( t , i ) ) ;
        ans[i] = -1 ;
    }
    pre_dfs ( 1 , 0 ) ;
    for ( int i = 1 ; i <= n ; ++ i ) {
        cnt = 0 ;
        dfs ( i , pre[i] ) ;
        sort ( b + 1 , b + cnt + 1 ) ;
        for ( int j = 0 ; j <= cnt + 1 ; ++ j ) {
            for ( int k = 0 ; k < LIM ; ++ k ) {
                dp[j][k] = inf ;
                dp2[j][k] = -1 ;
            }
        }
        sumc[0] = sumv[0] = 0 ;
        for ( int j = 1 ; j <= cnt ; ++ j ) {
            sumc[j] = sumc[j - 1] + b[j].c ;
            sumv[j] = sumv[j - 1] + b[j].v ;
        }
        dp[0][0] = 0 ;
        dp2[cnt + 1][0] = 0 ;
        for ( int j = 0 ; j < cnt ; ++ j ) {
            for ( int k = 0 ; k < LIM ; ++ k ) if ( dp[j][k] < inf ) {
                dp[j + 1][k] = min ( dp[j + 1][k] , dp[j][k] ) ;
                if ( k + b[j + 1].c < LIM ) dp[j + 1][k + b[j + 1].c] = min ( dp[j + 1][k + b[j + 1].c] , dp[j][k] + b[j + 1].v ) ;
            }
        }
        for ( int j = cnt + 1 ; j >= 2 ; -- j ) {
            for ( int k = 0 ; k < LIM ; ++ k ) if ( ~dp2[j][k] ) {
                dp2[j - 1][k] = max ( dp2[j - 1][k] , dp2[j][k] ) ;
                if ( k + b[j - 1].c < LIM ) dp2[j - 1][k + b[j - 1].c] = max ( dp2[j - 1][k + b[j - 1].c] , dp2[j][k] + b[j - 1].v ) ;
            }
        }
        for ( int j = 0 ; j < Q[i].size () ; ++ j ) {
            int idx = Q[i][j].second , t = Q[i][j].first ;
            if ( t > sumc[cnt] ) {
                ans[idx] = -1 ;
                continue ;
            }
            if ( t < LIM ) {
                ans[idx] = dp2[1][t] ;
            } else {
                int x = F ( t , 1 , cnt ) ;
                int del = sumc[x] - t ;
                for ( int k = del ; k < LIM ; ++ k ) {
                    if ( dp[x][k] < inf && ~dp2[x + 1][-del + k] ) {
                        ans[idx] = max ( ans[idx] , sumv[x] - dp[x][k] + dp2[x + 1][-del + k] ) ;
                    }
                }
            }
        }
    }
    for ( int i = 1 ; i <= q ; ++ i ) {
        printf ( "%lld\n" , ans[i] ) ;
    }
}

int main () {
    int T ;
    scanf ( "%d" , &T ) ;
    for ( int i = 1 ; i <= T ; ++ i ) {
        printf ( "Case #%d:\n" , i ) ;
        solve () ;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值