也是树形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;
}

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

被折叠的 条评论
为什么被折叠?



