有依赖的背包问题又是一个经典的背包延伸问题,理解她可以让我们更深刻地理解背包中三重循环的顺序逻辑,同时她也是树形动规的雏形。
题目一般情形:有 N N N 个物品和一个容量是 V V V 的背包。物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。每件物品的编号是 i i i,体积是 v i v_i vi,价值是 w i w_i wi,依赖的父节点编号是 p i p_i pi。物品的下标范围是 1 … N 1…N 1…N。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。
动规解法:
1、状态表示:
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以
i
i
i 为根节点的子树背包体积为
j
j
j 的最大价值
2、状态转移:
f
[
i
]
[
j
]
=
m
a
x
{
f
[
i
]
[
j
−
k
]
+
f
[
s
o
n
(
i
)
]
[
k
]
}
,
0
⩽
k
⩽
j
,
0
⩽
j
⩽
V
−
v
i
f[i][j]=max\{f[i][j-k]+f[son(i)][k]\},0\leqslant k\leqslant j,0\leqslant j \leqslant V-v_i
f[i][j]=max{f[i][j−k]+f[son(i)][k]},0⩽k⩽j,0⩽j⩽V−vi
代码与注释:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=110;
int f[N][N],n,V;
int ne[N],e[N],idx,h[N];
int v[N],w[N];
void add(int p,int i){ //邻接表存图,数组模拟链表
e[idx]=i;
ne[idx]=h[p];
h[p]=idx++;
}
void dfs(int u){ //dfs相当于物品,然后j是循环体积,k是循环决策
for(int i=h[u];i!=-1;i=ne[i]){
int son=e[i];
dfs(son);
for(int j=V-v[u];j>=0;j--) //预留体积
for(int k=0;k<=j;k++)
f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]);
}
for(int i=V;i>=v[u];i--) f[u][i]=f[u][i-v[u]]+w[u];
for(int i=0;i<v[u];i++) f[u][i]=0;
}
int main(){
cin>>n>>V;
int root;
memset(h,-1,sizeof(h));
for(int i=1;i<=n;i++){
int p;
cin>>v[i]>>w[i]>>p;
if(p==-1) root=i; //-1为根节点
else add(p,i);
}
dfs(root);
cout<<f[root][V]<<endl;
return 0;
}