线性扩展到树
考虑前i个物品-> 考虑以u为根节点的子树
从前往后-> 从下往上
将每棵子树根据体积,看作有m + 1中选择方式的分组背包问题
这样就避免了枚举子树的所有不同种类的选法,例如当前子树除根节点外有k个节点,最坏情况下的时间复杂度为O(2k)
本质:树形DP + 分组背包
- 整个框架是树形DP
- 递归求每个子树是分组背包
- 对于根节点来说,每个子树相当于一个分组(按体积)
理解:
单看两层树结构,包含根节点和一些叶节点,其中根节点是必选的。问题就变成了要么什么都不选,要么根节点必选剩余叶节点组成01背包问题
两层扩展至三层,对于中间一层来讲,已经求出每棵子树各个体积下对应的最大价值,而且是一一对应,此时对于根节点来讲,问题变成了要么什么都不选,要么根节点必选剩余子树组成分组背包问题,每个分组由不同体积对应的最大价值组成。
背包问题_8.pdf :::danger PDF的内容是第一次学习的笔记,已经很陈旧了,以下面的代码为准,更易懂一些! :::
AcWing 10. 有依赖的背包问题
题目描述
有 N 个物品和一个容量是 V 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
每件物品的编号是 i ,体积是v[i],价值是w[i],依赖的父节点编号是p[i]。物品的下标范围是1…N
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品个数和背包容量。
接下来有 N 行数据,每行数据表示一个物品。
第 i 行有三个整数v[i],w[i],p[i],用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 p[i]=−1,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例
11
思路:
树形DP + 分组背包
时间复杂度:O(NV2)
import java.util.*;public class Main {static final int N = 110;static int[][] f = new int[N][N];static int[] h = new int[N], e = new int[N], ne = new int[N];static int[] v = new int[N], w = new int[N];static int n, m, idx;static void add(int a, int b) {e[idx] = b;ne[idx] = h[a];h[a] = idx++;}public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt();m = sc.nextInt();Arrays.fill(h, -1);int root = 0;for (int i = 1; i <= n; i++) {v[i] = sc.nextInt();w[i] = sc.nextInt();int p = sc.nextInt();if (p == -1) root = i;else {add(p, i);}}dfs(root);System.out.println(f[root][m]);}static void dfs(int u) {for (int j = v[u]; j <= m; j++) {f[u][j] = w[u];}for (int i = h[u]; i != -1; i = ne[i]) {int son = e[i];dfs(son);for (int j = m; j >= v[u]; j--) { //注意这里是从大到小枚举for (int k = 0; k <= j - v[u]; k++) {f[u][j] = Math.max(f[u][j], f[u][j - k] + f[son][k]);}}}}}
