题目连接:http://poj.org/problem?id=2486
题意:
一颗树,n个点,n-1条边,每个点上有一个权值,求从1出发,走k步,最多能遍历到的权值。
思路:
很容易想到设dp[x][j]表示x子树分配j步能获得最多苹果数量,但是这样是不够的,因为很容易发现只有哪些回到了x点的方案的代价才是子树大小*2,但是走到最后的时候其实可以不用回到x点。
那么我们就增加状态纬度,设dp[i][j][0/1]:给树i恰好分配j步(回/不回来)i点的最大值。然后分3中情况写状态转移方程:
dp[x][j][0]=max(dp[x][j][0],dp[x][j-k][0]+dp[y][k-2][0]); //情况①:表示遍历从左边的子树回来x点之后,去y子树,再回来x点。
dp[x][j][1]=max(dp[x][j][1],dp[x][j-k][0]+dp[y][k-1][1]); //情况②:表示从左边回来,去y子树,不回来x点了
dp[x][j][1]=max(dp[x][j][1],dp[x][j-k][1]+dp[y][k-2][0]); //情况3:表示从y子树回来,去左边,不回来x点了
#include <iostream> #include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include <iomanip> #include <time.h> #include <bitset> #include <cmath> #define LL long long #define INF 0x3f3f3f3f #define ls nod<<1 #define rs (nod<<1)+1 const double eps = 1e-10; const int maxn = 100 + 10; const LL mod = 1e9 + 7; int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;} using namespace std; struct edge { int v,nxt; }e[maxn<<1]; int head[maxn]; int cnt; int f[maxn][maxn<<1][2]; int n,m; inline void add_edge(int u,int v) { e[++cnt].v = v; e[cnt].nxt = head[u]; head[u] = cnt; } inline void dfs(int x,int fa) { for (int i = head[x];~i;i = e[i].nxt) { int v = e[i].v; if (v == fa) continue; dfs(v,x); for (int j = m;j >= 1;j--) { for (int k = 0;k <= j;k++) { if (k>=2) f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+f[v][k-2][0]); if (k>=1) f[x][j][1]=max(f[x][j][1],f[x][j-k][0]+f[v][k-1][1]); if (k>=2) f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+f[v][k-2][0]); } } } } int main() { while (cin >> n >> m) { cnt = 0; memset(head,-1, sizeof(head)); memset(f,-0x3f, sizeof(f)); for (int i = 1;i <= n;i++) { int val; cin >> val; f[i][0][1] = f[i][0][0] = val; } for (int i = 1;i < n;i++) { int u,v; cin >> u >> v; add_edge(u,v); add_edge(v,u); } dfs(1,0); int ans=0; for (int i=0;i<=m;i++) ans=max(ans,max(f[1][i][0],f[1][i][1])); cout << ans << endl; } return 0; }