HDU 4276 The Ghost Blows Light

本文详细介绍了一道名为TheGhostBlowsLight的树形DP题目。文章首先介绍了如何通过预处理找到从根节点到目标节点的路径,并利用分组背包的思想解决在路径上节点的最优选择问题。文中还提供了完整的C++代码实现。

The Ghost Blows Light

也是树形DP里非常不错的一道题。由于要从1走到N,我们可以先将从1到N的最短路给直接处理出来。然后由于我们可以在这条路上的一个节点走一个回路,已得到更大的价值。所以我们可以再对每个点进行一个分组背包。dp1[i][j]表示从i点出发回到i点用时j所能得到的最大价值。然后在进行一次分组背包dp2[i][j] i是关键路径上的点,表示从1走到i点用时j所能获得最大价值。方程很容易写。。。。


#include <cstdio>
#include <cstring>
#include <vector>
using std::vector ;
#define maxn 105

struct Node{
    int e ;
    int w ;
    Node(int ee = 0 , int ww = 0)
    :e(ee) , w(ww){} ;
};

vector<Node> G[maxn] ;
int val[maxn]  ;
Node next[maxn] ;//找出从1到n的必经路

int dp1[maxn][maxn * 5] ;//dp1[i][j]表示从i出发用时j分钟回到i的最大价值
int dp2[maxn][maxn * 5] ;
int n ;
int T ;

void init(){

    for(int i = 0 ; i <= n ; i ++){
        G[i].clear() ;
    }
    memset(next , -1 , sizeof(next)) ;
    memset(dp1 , 0 , sizeof(dp1)) ;
    memset(dp2 , -1, sizeof(dp2)) ;
}

void read(){
    int p , q , w ;
    for(int i = 1 ; i < n ; i ++){
        scanf("%d%d%d" , &p , &q , &w) ;
        G[p].push_back(Node(q , w)) ;
        G[q].push_back(Node(p , w)) ;
    }
    for(int i = 1 ; i <= n ; i ++){
        scanf("%d" , &val[i]) ;
    }
}

void dfs(int v , int fa){
    for(vector<Node> :: size_type i = 0 ; i != G[v].size() ; i ++){
        int u = G[v][i].e ;
        if(u == fa)
            continue ;
        dfs(u , v) ;
    }
    //寻找路径
    if(v == n || next[v].e != -1){
        next[fa].e = v ;
    }

    for(vector<Node> :: size_type i = 0 ; i != G[v].size() ; i ++){
        int u = G[v][i].e ;
        int w = G[v][i].w ;

        if(u == fa ){
            if(next[fa].e == v)
                next[fa].w = w ;
            continue ;
        }
        if(u == next[v].e)
            continue ;

        for(int j = T ; j >= 0 ; j --){
            for(int k = 0 ; k <= j - 2 * w ; k ++){
                if( dp1[v][j] < dp1[u][k] + dp1[v][ j - k - 2 * w ] ){
                    dp1[v][j] = dp1[u][k] + dp1[v][ j - k - 2 * w ] ;
                }
            }
        }
    }

    for(int j = 0 ; j <= T ; j ++){
        dp1[v][j] += val[v] ;
    }
}

void solve(){
    //caculate the value of dp1[i][j] ;
    dfs(1 , 0 ) ;

    for(int i = 0 ; i <= T ; i ++)
        dp2[1][i] = dp1[1][i] ;

    int total ;
    total = 0 ;

    for(int i = 1 ; i != n ; i = next[i].e){
        int u = next[i].e ;
        int w = next[i].w ;
        total += w ;
        for(int t = T ; t >= 0 ; t --){
            for(int k = 0 ; k <= t - total ; k ++){
                if(dp2[u][t] < dp2[i][t - k - w] + dp1[u][k] )
                    dp2[u][t] = dp2[i][t - k - w ] + dp1[u][k] ;
            }
        }
    }

    if(dp2[n][T] == -1){
        puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!") ;
    }
    else{
        printf("%d\n" , dp2[n][T]) ;
    }
}

int main(){
    //freopen("4276.txt" ,"r" ,stdin) ;
    while( scanf("%d%d" , &n , &T) != EOF ){
        init()  ;
        read()  ;
        solve() ;
    }
    return 0;
}


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值