【题意】一颗有n个节点的树,每个节点有val值和c(有无陷阱)值,给出最大可踩陷阱数m。在树中任取一点作为起点,经过某点就取得该点的val值,踩到第m个陷阱后马上停止,而且不能走已走过的点,求所能获得的最大val总值。
【解题方法】树形dp,经典题,虽然我不会。 dp[u][i][0/1]:=以u为根的子树中,从有无陷阱的节点出发,且经过i个陷阱的最大权值和
我们来考虑下ans的构成,枚举2颗子树接起来,然后就是枚举各自经过的陷阱数,边界情况很多−−
需要注意的是枚举其中一颗子树不能包括另一个子树,树形dp常见技巧了 更新的时候,对于dp[v][j][0]要往
对于dp[v][j][1]要往dp[u][j+trap[u][1]更新的话,j可以=c,因为dp[u][c][1]构成答案的时候,可以作为终点
【AC code】
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
int n,c,val[maxn],trap[maxn];
ll dp[maxn][5][2];//u为根子树,经过j陷阱,起点有(0)无(1)陷阱
vector<int>G[maxn];
ll ans;
void getmax(ll &a,ll b){
if(b>a) a=b;
}
void dfs(int u,int fa){
dp[u][trap[u]][trap[u]]=val[u];
for(int v:G[u]){
if(v==fa) continue ;
dfs(v,u);
for(int i=0; i<=c; i++){
for(int j=0; i+j<=c; j++){
getmax(ans,dp[u][i][1]+dp[v][j][1]);
if(i<c) getmax(ans,dp[u][i][0]+dp[v][j][1]);
if(j<c) getmax(ans,dp[u][i][1]+dp[v][j][0]);
if(i+j<c) getmax(ans,dp[u][i][0]+dp[v][j][0]);
}
}
for(int i=0; i<=c; i++){
if(i<c) getmax(dp[u][i+trap[u]][0],dp[v][i][0]+val[u]);
getmax(dp[u][i+trap[u]][1],dp[v][i][1]+val[u]);
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&c);
memset(dp,-0x3f,sizeof(dp));
for(int i=1; i<=n; i++) G[i].clear();
for(int i=1; i<=n; i++){
scanf("%d%d",&val[i],&trap[i]);
}
int u,v;
for(int i=1; i<n; i++){
scanf("%d%d",&u,&v);
G[++u].push_back(++v);
G[v].push_back(u);
}
ans=0;
dfs(1,-1);
printf("%I64d\n",ans);
}
}