题意为:给你一颗树,每个节点有价值和花费,且每当选择一个节点后,其父亲与祖先节点不能再选,给定限制花费为M,求能获得的最大价值。
俗话说的好,dp想不出来了先想dfs,对于这一道题,如果我们进行dfs,那么状态为pos表示现在的节点,rest还能花的钱,返回值为对于这两个参数的最大价值。那么对于每一个节点,我们便很容易想到转移,一种为只选自己所得到的价值,或者就是给每一棵子树分一定的价值,所获得的最大价值,如果搜索到了叶子节点,那么答案就是自己本身。 用代码实现为
int dfs(int pos,int rest)
{
if(edge[pos].size()==0)
{
if(rest<v[pos]) return 0;
else return w[pos];
}
for(int i=0;i<edge[pos].size();i++)
{
给每一个儿子一定的值ki 且 ( sigma ki) ==rest
sum+=dfs(edge[pos][i],k);
}
if(rest<v[pos]) return sum;
else return max(w[pos],sum);
}
这时,我们就可以考虑dp了。
显而易见,对于每一个叶子节点,都能转移到它的父亲节点,那么我们就可以使用dp[i][j] 表示考虑到了前i个时,剩余大小为j的最优答案。
可是要如何转移呢?我们需要看见题目上一个隐秘的性质即员工的编号会大于他的直接上司的编号,所以可以直接进行倒序枚举。
根据我们的dfs方程,我们便可以得出,对于每一个点,如果剩余量大于等于它的花费那么答案至少是它自己,那么每进入一个点,先枚举出求出这一步即dp[i][j]=max(dp[i][j],w[i]) j>=v[i]。
接着是对于每个点的转移,先考虑如果我在dp[i][j]可以转移到哪个点,由dfs可知,每个点可以推回它的父亲节点,之后再枚举给这个点多少剩余的价值,即可完成这一步转移,即dp[fa[i]][j]=max(dp[fa[i]][j],dp[fa[i]][j-k]+dp[i][k]);
下附AC代码。
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define maxn 1030
using namespace std;
int n,m;
int dp[maxn][115];
int fa[maxn];
int v[maxn];
int w[maxn];
int main()
{
cin>>n>>m;
for(int i=2;i<=n;i++)
cin>>fa[i];
for(int i=1;i<=n;i++)
cin>>v[i];
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=n;i>=1;i--)
{
for(int j=m;j>=v[i];j--)
dp[i][j]=max(dp[i][j],w[i]);
for(int j=m;j>=0;j--)
for(int k=0;k<=j;k++)
{
dp[fa[i]][j]=max(dp[fa[i]][j],dp[fa[i]][j-k]+dp[i][k]);
}
}
cout<<dp[1][m]<<endl;
}