题意:
一颗树,n个点(1-n),n-1条边,每个点上有一个权值,求从1出发,走k步,最多能遍历到的权值
思路:
1.我们想要回到根节点。
把这个过程分为两个部分:
step1:从根节点x到达son,从son遍历son的子树再回到son,从son回到son的根节点x;
step2:用剩余的步数遍历根节点x其余的子树,再回到根节点x.
获得的价值:dp[son][s-2][0] + dp[x][j - s][0]
为什么是s - 2呢,因为除过son的分支外,根的其他分支已经用去了j-s步,到son只剩下s步,而来回从根到son需要两步。
所以在以son为根的子树中走的就是s-2步;
我们得到: dp[x][j][0] = max(dp[x][j][0] , dp[son][s-2][0] + dp[x][j - s][0])
2.不一定回到根节点。
既然不一定回到根节点x,那么最后的位置会是哪里呢?
首先要把以当前决策的子节点son为根节点的子树和根节点x的其余子树分开来考虑。
case1:
最后的位置在以子节点son为根节点的树上。
那么:
step1:从根节点遍历其余的子树,最终回到根节点;
step2:从根节点遍历以son为根节点的树,最终不一定回到son.
我们得到: dp[x][j][1] = max(dp[x][j][1] , dp[son][s-1][1] + dp[x][j - s][0])
多出的那个“1”和上面类似,是从根节点x到子节点son的一步。
case2:
最后的位置在其余的子树上。
step1:从根节点遍历以son为根节点的子树,最终回到son,并回到根节点x.
strep2:从根节点出发遍历其余的子树,最终不一定回到son,不知所踪。
我们得到 : dp[x][j][1] = max(dp[x][j][1] , dp[son][s-2][0] + dp[x][j- s][1])
状态转移方程大功告成。
还有因为题目没有说必须走k步,所以每个节点都初始化为a[x];
ac代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100+10;
int dp[maxn][maxn*2][2];
int head[maxn*2];
int cnt;
int a[maxn];
int n,k;
struct node{
int v;
int next;
int w;
}p[maxn*2];
inline void addedge(int u,int v){
p[++cnt].v=v;
p[cnt].next=head[u];
head[u]=cnt;
}
inline void init(){
cnt=0;
memset(head,-1,sizeof(head));
memset(dp,0,sizeof(dp));
}
void dfs(int x,int fa){
for(int i=0;i<=k;i++){
dp[x][i][0]=dp[x][i][1]=a[x];
}
for(int e=head[x];e!=-1;e=p[e].next){
int son=p[e].v;
if(son==fa) continue;
dfs(son,x);
for(int j=k;j>=0;j--){
for(int s=1;s<=j;s++){
if(s>=2){
dp[x][j][0]=max(dp[x][j][0],dp[x][j-s][0]+dp[son][s-2][0]);
}
dp[x][j][1]=max(dp[x][j][1],dp[x][j-s][0]+dp[son][s-1][1]);
if(s>=2){
dp[x][j][1]=max(dp[x][j][1],dp[x][j-s][1]+dp[son][s-2][0]);
}
}
}
}
}
int main(){
while(scanf("%d%d",&n,&k)!=EOF){
init();
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs(1,0);
printf("%d\n",max(dp[1][k][0],dp[1][k][1]));
}
return 0;
}