题目链接:https://www.luogu.com.cn/problem/P2014
一道树上关于点的0-1背包问题。
这道题背包容量实际为m,但是可以让背包容量为m+1,因为0这个似虚非虚的根是一定要选的,并且它的贡献为0。
那么方程式:$dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k])$
其中,dp[u][j]表示以u为根,子树节点数为j的最大值。初始化的话:sum[u]=1,dp[u][1]=val[u],这是显然的。
不得不说树形DP的边界都好奇特。
AC代码:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 using namespace std; 5 const int N=400; 6 struct node{ 7 int to,next; 8 }edge[N]; 9 int head[N],tot; 10 int dp[N][N],sum[N],n,m; 11 void add(int u,int v){ 12 edge[tot].to=v; 13 edge[tot].next=head[u]; 14 head[u]=tot++; 15 } 16 void DFS(int u,int fa){ 17 sum[u]=1; 18 for(int i=head[u];i!=-1;i=edge[i].next){ 19 int v=edge[i].to; 20 if(v==fa) continue; 21 DFS(v,u); 22 sum[u]+=sum[v]; 23 for(int j=min(m+1,sum[u]);j>=1;j--){ 24 for(int k=min(j-1,sum[v]);k>=0;k--){ 25 dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]); 26 } 27 } 28 } 29 } 30 int main(){ 31 memset(head,-1,sizeof(head)); 32 scanf("%d%d",&n,&m); 33 for(int i=1;i<=n;i++){ 34 int s,k; 35 scanf("%d%d",&k,&s); 36 add(k,i); dp[i][1]=s; 37 } 38 DFS(0,-1); 39 printf("%d ",dp[0][m+1]); 40 return 0; 41 }