输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。
接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数 vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例
11
背包问题循环顺序:
1.循环物品
2.循环体积
3.循环决策
思路:使用邻接表把树的各个节点存起来,之后进行dfs对树的节点进行遍历,因为每件物品只能选一次,所以采用0-1背包的思想:for(int j=m-v[u];j>=0;j–) 从后往前遍历,dp方程f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]);
#include<bits/stdc++.h>
using namespace std;
const int N =110;
int h[N],e[N],ne[N],idx;
int v[N],w[N],f[N][N];
int n,m;
//使用邻接表对边的情况进行存储
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u)
{
for(int i=h[u];i!=-1;i=ne[i]) //遍历当前节点u的子节点(循环物品)
{
int son = e[i]; //要求得节点u ans,必须知道子节点son ans,进行dfs搜索
dfs(son);
for(int j=m-v[u];j>=0;j--) //循环体积
{
/*
对比01背包for(int j=V; j>=v[i]; --j)
u必选所有情况必须留下v[u],不然u装不进背包里
*/
for(int k=0;k<=j;k++) //循环决策
{
/*
子树to(以to为root的子树)有不同的选点方案。
这些方案可以视为同一分组的不同物品(相对于u节点来说),则子树u有出度个不同分组。
虽然不知道具体的方案,但子树不同的体积就类似于不同方案。(不同选取体积不同)
那么就可以将子树的不同体积视为同一分组的具体物品。
就将问题转换到分组背包问题
对于dp[u][j]而言,他可以不选,或者从to的某一体积转移过去
*/
f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]);
}
}
for(int j=m;j>=v[u];j--) f[u][j]=f[u][j-v[u]]+w[u]; //加上刚刚默认选择的父节点价值
for(int j=0;j<v[u];j++) f[u][j]=0; //体积连v[u]都不足表示连根节点都装不下,赋值为0
}
int main()
{
memset(h,-1,sizeof h);
int root;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int p;
cin>>v[i]>>w[i]>>p;
if(p==-1) root=i; //如果p为-1,则其为根节点
else add(p,i);
}
dfs(root);
cout<<f[root][m]<<endl; //输出以root为根,体积为m时的最大价值
return 0;
}