dp[i][j][0] 代表走到第i个点 还能走j步 最后返回i的最大价值
dp[i][j][1] 代表走到第i个点 还能走j步 最后不返回i的最大价值
一般目标来回往返并每步都有消耗的题都是这种记录往返标识的dp
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define maxn 204
using namespace std;
int dp[maxn][maxn][2], va[maxn];
int last[maxn * 2], Next[maxn * 2], edge[maxn * 2], l;
int n, k;
void add(int a, int b)
{
edge[l] = b;
Next[l] = last[a];
last[a] = l;
l++;
}
void dfs(int pre, int fa)
{
for(int i = 0; i<= k; i++)
{
dp[pre][i][0] = va[pre];
dp[pre][i][1] = va[pre];
}
for(int i = last[pre]; i != -1; i = Next[i])
if(fa != edge[i])
{
dfs(edge[i], pre);
for(int j = k; j >= 0; j--)
for(int k1 = 0; k1 <= j; k1++)
{
if(k1 + 2 <= j)
{
dp[pre][j][0] = max(dp[pre][j][0], dp[pre][j - k1 - 2][0] + dp[edge[i]][k1][0]); //更新最后回到i的
dp[pre][j][1] = max(dp[pre][j][1], dp[pre][j - k1 - 2][1] + dp[edge[i]][k1][0]);//最后不回到i记录这条往返的
}
if(k1 < j) dp[pre][j][1] = max(dp[pre][j][1], dp[pre][j - k1 - 1][0] + max(dp[edge[i]][k1][0], dp[edge[i]][k1][1]));//以这条边为最后出度更新最后不回到i的最优价值
}
}
}
int main()
{
while(scanf("%d%d", &n, &k)!=EOF)
{
memset(last, -1, sizeof(last));
memset(dp, 0, sizeof(dp));
l = 0;
for(int i = 1; i <= n; i++) scanf("%d", &va[i]);
for(int i = 1; i < n; i++)
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
add(b, a);
}
dfs(1, 0);
int ans = 0;
printf("%d\n", max(dp[1][k][0], dp[1][k][1]));
}
return 0;
}