n个点各有权值val [ i ], 总时间为T,n-1条双向边,u->v,从u走到v 花费为w, 求在花费不超过T的情况下,能获得的经过的各点权值之和最大。
分析:首先预处理出一条从点1 ~ N的最短路, 那么最优路径的主干一定是经过该最短路的。然后是否走其他的点取决于是否有多出的时间(很显然。)。而且经过最短路之外的点的话,那些点要么不走要么走且只走两次(来回)。
所以先预处理出一条最短路之后,最短路上的点权值改为0,然后用树形dp解决是否要取旁边的点。设dp [ u ][ j ]为走到 u 时花费为 j ,那么
dp[u][j] = max(dp[u][j], dp[v][k] + dp[u][j - weit - k]);
完整代码如下:
#include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> #define N 105 using namespace std; int a[N]; struct node{ int to, nxt, cost; }edge[N * 3]; int head[N]; int dp[N][N * 5]; int cnt; int n, t; int tt; void init() { cnt = 0; memset(head, -1, sizeof(head)); } void addedge(int u, int v, int w) { edge[cnt].to = v; edge[cnt].cost = w; edge[cnt].nxt = head[u]; head[u] = cnt++; } int dfs1(int u, int pre) { if(u == n) { return 1; } for( int i = head[u]; ~i; i = edge[i].nxt ) { int v = edge[i].to; if(v == pre) continue; if(dfs1(v, u)) { tt += edge[i].cost; edge[i].cost = 0; return 1; } } return 0; } void dfs2(int u, int pre) { for(int i = 0; i <= t; i++) dp[u][i] = a[u]; for( int i = head[u]; ~i; i = edge[i].nxt ) { int v = edge[i].to; if(v == pre) continue; dfs2(v, u); int weit = edge[i].cost * 2; for(int j = t; j >= weit; j--) for(int k = 0; k <= j - weit; k++) dp[u][j] = max(dp[u][j], dp[v][k] + dp[u][j - weit - k]); } } int main() { while(~scanf("%d%d", &n, &t)) { init(); for(int i = 1; i < n; i++) { int u, v, w; scanf("%d%d%d", &u, &v, &w); addedge(u, v, w); addedge(v, u, w); } for(int i = 1; i <= n; i++) scanf("%d", &a[i]); tt = 0; dfs1(1, -1); if(tt > t) { puts("Human beings die in pursuit of wealth, and birds die in pursuit of food!"); continue; } t -= tt; dfs2(1, -1); printf("%d\n", dp[1][t]); } return 0; }