这是一道比较特别的树形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;
}
本文介绍了一种特殊的树形动态规划问题及其解决方法。该问题涉及在树状结构中寻找最优路径,并考虑路径上的陷阱数量限制。文章通过详细的代码示例展示了如何递归地更新节点状态并求解最大值。
1569

被折叠的 条评论
为什么被折叠?



