题意
一棵树,树上有N个点,能走K步。第二行有N个数字,代表着N个点上的苹果数。后面N-1行代表苹果树的联通情况。问最多能得到多少个苹果。
题解
又是一道树形DP的题目,不过这道题不太好搞。这道题涉及的情况还是比较复杂的,需要十分缜密的思维。首先我们可以把状态转移方程设置为dp[i][j][k],i代表当前点,j代表当前状态还可走的步数,k分两种状态,0代表回到当前点,1代表不回到当前点。
对于回到当前点的情况,还是比较容易分析的。dp[u][j][0]=max(dp[u][j][0],dp[u][j-s-2][0]+dp[v][s][0])。因为向下走一步和回来分别消耗了1步,因此便需要j-s-2,这代表从某个点回来以后还可以访问其他的点,从其他的点再回来。
对于不回到当前点的情况,就有些难分析了,稍有不慎,很容易就会WA。不回到当前点,分两种状态转移情况,一种情况是从别的点回来以后再访问下一个点,下一个点不再回来。这个对应状态转移方程dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-1][0]+dp[v][s][1])。另一种情况是当前这个点回来,选择前面访问过的点作为不回来的点。这个对应状态转移方程dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-2][1]+dp[v][s][0])。只有考虑完整了这两个状态,才能保证结果是最大的。
代码
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<string>
#include<set>
#include<map>
#include<bitset>
#include<stack>
#include<string>
#define UP(i,l,h) for(int i=l;i<h;i++)
#define DOWN(i,h,l) for(int i=h-1;i>=l;i--)
#define W(a) while(a)
#define MEM(a,b) memset(a,b,sizeof(a))
#define LL long long
#define INF 0x3f3f3f3f
#define MAXN 800010
#define MOD 1000000007
#define EPS 1e-3
using namespace std;
int dp[110][210][2];
vector<int> vc[110];
int n,k;
int val[110];
void dfs(int u,int p) {
int sz=vc[u].size();
UP(i,0,k+1) {
dp[u][i][0]=dp[u][i][1]=val[u];
}
UP(i,0,sz) {
int v=vc[u][i];
if(v==p)
continue;
dfs(v,u);
DOWN(j,k+1,1) {
// cout<<j<<endl;
UP(s,0,k+1) {
if(j-s-2>=0) {
dp[u][j][0]=max(dp[u][j][0],dp[u][j-s-2][0]+dp[v][s][0]);
dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-2][1]+dp[v][s][0]);
}
if(j-s-1>=0) {
dp[u][j][1]=max(dp[u][j][1],dp[u][j-s-1][0]+dp[v][s][1]);
}
// cout<<u<<" "<<s<<" "<<v<<" "<<dp[u][j][1]<<endl;
}
}
}
}
int main() {
W(~scanf("%d%d",&n,&k)) {
MEM(dp,0);
MEM(vc,0);
UP(i,1,n+1) {
scanf("%d",&val[i]);
}
UP(i,1,n) {
int a,b;
scanf("%d%d",&a,&b);
vc[a].push_back(b);
vc[b].push_back(a);
}
dfs(1,-1);
printf("%d\n",max(dp[1][k][0],dp[1][k][1]));
}
}
/*
5 5
2 15 5 15 10
1 2
2 3
2 4
3 5
5 5
0 3 4 4 1
1 2
1 3
3 4
2 5
*/