这是一道比较特别的树形dp题目。。。。。。。或许只是我自己这样认为的吧。
1.假如u为根节点,v1,v2,v3是其子节点,v1与v2可以连成一条链,不仅仅只是父节点与子节点可以连成一条链(其实就是可以任意选取点开始)。
2。题目要求中了几个陷阱就要停止,与“最多能中的陷阱不超过n”这个条件有较大不同,要考虑所选取链头尾是否有陷阱(当链的总陷阱数达到n时)。
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 50005
const long long INF=1LL<<61;
int n,c;
vector<int> e[maxn];
int val[maxn],trap[maxn];
long long int ans,dp[maxn][4][2];//第一维表示节点,第二维表示中了陷阱的个数,第三维表示起点是否有陷阱,1,代表有,0代表没有
void dfs(int u,int p)
{
int s=e[u].size();
dp[u][trap[u]][trap[u]]=val[u];
ans=max(ans,dp[u][trap[u]][trap[u]]);
for (int i=0;i<s;i++)
{
int v=e[u][i];
if (v==p) continue;
dfs(v,u);
for (int j=0;j<=c;j++)
for (int k=0;k+j<=c;k++)
{
if (k+j<=c) ans=max(ans,dp[u][j][1]+dp[v][k][1]);
if (k+j<c) ans=max(ans,dp[u][j][0]+dp[v][k][0]);
if (j!=c) ans=max(ans,dp[u][j][0]+dp[v][k][1]);
if (k!=c) ans=max(ans,dp[u][j][1]+dp[v][k][0]); //这几个转移表示在两个子节点选取链,当然要避免dp[x][c][0]的情况出现。
}
for (int j=0;j<c;j++)
{
dp[u][j+trap[u]][0]=max(dp[u][j+trap[u]][0],dp[v][j][0]+val[u]);
dp[u][j+trap[u]][1]=max(dp[u][j+trap[u]][1],dp[v][j][1]+val[u]);//由子节点更新父节点。
}
if (!trap[u]) dp[u][c][1]=max(dp[u][c][1],dp[v][c][1]+val[u]);
}
}
int main()
{
int cas,u,v;
scanf("%d",&cas);
while (cas--)
{
scanf("%d%d",&n,&c);
for (int i=0;i<n;i++) e[i].clear();
for (int i=0;i<n;i++)
scanf("%d%d",&val[i],&trap[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
e[u].push_back(v); e[v].push_back(u);
}
ans=0;
for (int i=0;i<n;i++)
for (int j=0;j<4;j++)
for (int k=0;k<2;k++)
dp[i][j][k]=-INF;
dfs(0,-1);
printf("%I64d\n",ans);
}
return 0;
}