题目地址:
https://www.acwing.com/problem/content/10/
有 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。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。
输入格式:
第一行有两个整数
N
N
N,
V
V
V,用空格隔开,分别表示物品个数和背包容量。接下来有
N
N
N行数据,每行数据表示一个物品。第
i
i
i行有三个整数
v
i
,
w
i
,
p
i
v_i,w_i,p_i
vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。如果
p
i
=
−
1
p_i=−1
pi=−1,表示根节点。数据保证所有物品构成一棵树。
输出格式:
输出一个整数,表示最大价值。
数据范围:
1
≤
N
,
V
≤
100
1≤N,V≤100
1≤N,V≤100
1
≤
v
i
,
w
i
≤
100
1≤v_i,w_i≤100
1≤vi,wi≤100
节点编号范围:
内部结点:
1
≤
p
i
≤
N
1≤p_i≤N
1≤pi≤N
根节点
p
i
=
−
1
p_i=−1
pi=−1
思路是动态规划,设 f [ u ] [ j ] f[u][j] f[u][j]是以 u u u为树根的子树里,包含树根的所有方案中,总体积不超过 j j j的那些方案的最大价值。那么这些方案可以按照第 i i i个子树选不选来分类,这样问题成为一个 0 − 1 0-1 0−1背包问题,对于每个子树 x x x,先递归求解 f [ x ] f[x] f[x],接着枚举该子树耗用的体积(这里可以看成是有体积分别是 0 , 1 , . . . , j − v u 0,1,...,j-v_u 0,1,...,j−vu,价值分别是 f [ x ] [ 0 ] , f [ x ] [ 1 ] , . . . , f [ x ] [ j − v u ] f[x][0],f[x][1],...,f[x][j-v_u] f[x][0],f[x][1],...,f[x][j−vu]的物品,我们枚举这些个物品选哪个),来更新 f [ u ] [ j − v u ] f[u][j-v_u] f[u][j−vu]。总的来说,这个问题像是做一个分组背包,每个子树看成是一个组,然后这个组里有若干种体积不同的物品,我们只能选其中一个(选体积 0 0 0的那个物品的时候,相当于不选这个组的物品)。枚举 f [ u ] f[u] f[u]的体积的时候可以模仿 0 − 1 0-1 0−1背包的方式,从大到小来枚举,这样可以只开一维,以节省空间。代码如下:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N];
// 邻接表建树
int h[N], e[N], ne[N], idx;
// f[u][j]的定义是,在以u为树根的子树里选,包含u的且总体积不超过j的所有方案中,最大价值是多少
int f[N][N];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
// 求解f[u]向量
void dfs(int u) {
// 枚举每个子树
for (int i = h[u]; i != -1; i = ne[i]) {
int son = e[i];
// 对于所有的k,递归求解f[son][k]
dfs(e[i]);
// 枚举体积
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[son][k]);
}
for (int i = m; i >= v[u]; i--) f[u][i] = f[u][i - v[u]] + w[u];
// 当i < v[u]的时候,树根不能取,也就不存在合法方案,赋值为0
for (int i = 0; i < v[u]; i++) f[u][i] = 0;
}
int main() {
cin >> n >> m;
memset(h, -1, sizeof h);
int root;
for (int i = 1; i <= n; i++) {
int p;
cin >> v[i] >> w[i] >> p;
if (p == -1) root = i;
else add(p, i);
}
dfs(root);
cout << f[root][m] << endl;
return 0;
}
时间复杂度 O ( N V 2 ) O(NV^2) O(NV2),空间 O ( N V ) O(NV) O(NV)。