Date:2022.03.29
题意描述:
有 N 个物品和一个容量是 V 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 1…N。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。
接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数 vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=−1,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
数据范围
1≤N,V≤100
1≤vi,wi≤100
父节点编号范围:
内部结点:1≤pi≤N;
根节点 pi=−1;
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例:
11
思路:题目有很强的限制即“选择它就必须选择它的父节点”,考虑树形dp,
f
[
u
]
[
j
]
:
f[u][j]:
f[u][j]:以
u
u
u为根的子树中,总体积不超过
j
j
j的最大价值。
如果暴力来看每个点的每个子结点,每个子节点都可选或不选,也就是
2
100
2^{100}
2100,这不t飞了?考虑优化。
观察到每个子结点都可以视作分组背包问题的一组,而对于这些组,背包的总体积是其父节点的第二属性。因此,我们考虑每个子节点为一个分组,在其中至多选择一个属性------给这个组分多少体积!因此对所有分组进行分组背包的遍历后,在每组选一个体积的最大总收益再加上子树的根节点
u
u
u就是
f
[
u
]
[
j
]
f[u][j]
f[u][j],之后就是树形dp。
需要注意,每次算完一个结点别忘了标记所有
f
[
u
]
[
k
]
【
其
中
k
<
v
[
u
]
】
f[u][k]【其中k<v[u]】
f[u][k]【其中k<v[u]】为不合法状态,因为装不下
u
u
u当然也装不下其子树。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
typedef long long LL;
LL n,m,f[N][N],t,k,v[N],w[N],root;
LL h[N],ne[N],e[N],idx;
void add(LL x,LL y)
{
e[idx]=y;ne[idx]=h[x];h[x]=idx++;
}
void dfs_down(LL u,LL fa)
{
for(int i=h[u];i!=-1;i=ne[i])
{
LL t=e[i];
dfs_down(t,u);
for(int j=m-v[u];j>=0;j--)
for(int k=0;k<=j;k++)
f[u][j]=max(f[u][j],f[u][j-k]+f[t][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;
}
int main()
{
cin>>n>>m;memset(h,-1,sizeof h);
for(int i=1;i<=n;i++)
{
LL idx;cin>>v[i]>>w[i]>>idx;
if(idx!=-1) add(idx,i);
else root=i;
}
dfs_down(root,-1);
cout<<f[root][m];
return 0;
}