题意描述:
给定一颗点上带权的树,走每条边要花费一定时间问在T分钟内从1走到n所能拿到的最大权和(n<=100, t<=500)。
分析:
数据范围很小,先dfs求出1到n至少需要多少时间,dis[ 1->n ] > T 那么走不到n。
否则把总时间减掉dis[1->n],在1->n路径上的点权值变成0 ,用剩下的时间从1开始跑树形背包,原因在于除了1->n路径上的边会被跑1次,其他的边要么不跑,要么跑两边 。
预处理算是预先把1->n的权和 和消耗的时间处理掉,
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
typedef long long ll;
typedef long long LL;
#define to first
#define val second
typedef pair<int,int> pii;
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define rep1(i,x,y) for(int i=x;i<=(int)y;i++)
const int N = 105;
const int T = 505;
vector<pii> G[N];
int d[N][T],dis[N],p[N],a[N];
void dfs(int u,int fa,int len){
dis[u]=len; p[u]=fa;
rep(i,G[u].size()) if(G[u][i].to!=fa){
dfs(G[u][i].to,u,G[u][i].val+len);
}
}
int n,t;
int dp(int u,int fa){
rep(i,G[u].size()){
int v=G[u][i].to,w=G[u][i].val*2;
if(v == fa) continue;
dp(v,u);
for(int i=t;i>=w;i--)
for(int j=w;j<=i;j++)
d[u][i]=max(d[u][i],d[u][i-j]+d[v][j-w]);
}
for(int i=0;i<=t;i++)
d[u][i]+=a[u];
}
int main()
{
while(scanf("%d %d",&n,&t)==2){
rep1(i,1,n) G[i].clear();
rep(i,n-1){
int x,y,v;
scanf("%d %d %d",&x,&y,&v);
G[x].push_back(pii(y,v));
G[y].push_back(pii(x,v));
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
dfs(1,-1,0);
if(dis[n] > t){
printf("Human beings die in pursuit of wealth, and birds die in pursuit of food!\n");
continue;
}
for(int i=n;p[i]!=-1;i=p[i]){
int u = i, v=p[i];
rep(j,G[u].size()) if(G[u][j].to==v){G[u][j].val=0; break;}
rep(j,G[v].size()) if(G[v][j].to==u){G[v][j].val=0; break;}
}
t-=dis[n];
memset(d,0,sizeof(d));
dp(1,-1);
printf("%d\n",d[1][t]);
}
return 0;
}