有依赖的背包问题

有依赖的背包问题

N 个物品和一个容量是 V 的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。

如下图所示:

如果选择物品 5,则必须选择物品 12。这是因为 25 的父节点,12 的父节点。

每件物品的编号是 i,体积是 vi,价值是 wi,依赖的父节点编号是 pi。物品的下标范围是 1N

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 NV,用空格隔开,分别表示物品个数和背包容量。

接下来有 N 行数据,每行数据表示一个物品。
i 行有三个整数 vi,wi,pi,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 pi=1,表示根节点。 数据保证所有物品构成一棵树。

输出格式

输出一个整数,表示最大价值。

数据范围

1N,V100
1vi,wi100

父节点编号范围:

  • 内部结点:1piN;
  • 根节点 pi=1;

输入样例

5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2

输出样例:

11

 

解题思路

  如果要选择第i个物品,那么就一定要选择第i个物品的根节点。也就是说每个物品都存在依赖关系,每个物品的选择都依赖于其父节点。

  对于任意一棵树,如果要从这棵树中选择物品,那么这棵树的根节点是一定要选择的。由根节点的每一个子节点所构成的子树,都是相互独立的,因此我们会自然想到枚举各个子树,求每个子树在不同体积限制下选择物品价值的最大值。

  对于一棵树所选择的物品,我们可以根据各个子树来进行划分,假设根节点有k个子节点。我们可以把选择的物品根据所属的子树分成k个部分。而在每个子树内部还可以继续细分成m+1个部分(m是限制的体积),分别是0m,表示从这棵子树中选择物品的体积最多是多少。

  因此问题变成了,一共有k棵子树,在每一棵子树中可以选择用多少体积,在这样的情况下求总体积不超过m的情况下的最大价值。可以发现这是一个分组背包问题。每一棵子树可以看成是一个分组,而组中的m+1个不同的体积可以看成是m+1种不同的物品,每个组都只能选择一种物品。  

  定义状态f(u,i,j)表示在以u为根的树中,从前i棵子树中选择体积不超过j且必选根节点u的所有方案的集合,属性是价值的最大值。状态转移方程为f(u,i,j)=max1vk{max0sjvu{f(u,i1,js)+f(v,size(v),s)}}

  其中v表示根节点u的子节点,size(v)表示v的子节点的个数,s表示在以v为根的子树中选择的物品体积不超过s

  AC代码如下,时间复杂度为O(nm2)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 110;
 5 
 6 int n, m;
 7 vector<int> g[N];
 8 int v[N], w[N];
 9 int f[N][N][N];
10 
11 void dfs(int u) {
12     for (int i = v[u]; i <= m; i++) {
13         f[u][0][i] = w[u];
14     }
15     for (int i = 1; i <= g[u].size(); i++) {
16         int t = g[u][i - 1];    // 子树根节点
17         dfs(t);
18         for (int j = v[u]; j <= m; j++) {
19             for (int k = 0; k <= j - v[u]; k++) {
20                 f[u][i][j] = max(f[u][i][j], f[u][i - 1][j - k] + f[t][g[t].size()][k]);
21             }
22         }
23     }
24 }
25 
26 int main() {
27     scanf("%d %d", &n, &m);
28     
29     int root;
30     for (int i = 1; i <= n; i++) {
31         int t;
32         scanf("%d %d %d", v + i, w + i, &t);
33         if (t == -1) root = i;
34         else g[t].push_back(i);
35     }
36     
37     dfs(root);
38     printf("%d", f[root][g[root].size()][m]);
39     
40     return 0;
41 }
复制代码

  还可以优化掉中间的一维,AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 110;
 5 
 6 int n, m;
 7 int head[N], e[N], ne[N], idx;
 8 int v[N], w[N];
 9 int f[N][N];
10 
11 void add(int v, int w) {
12     e[idx] = w, ne[idx] = head[v], head[v] = idx++;
13 }
14 
15 void dfs(int u) {
16     for (int i = v[u]; i <= m; i++) {
17         f[u][i] = w[u];
18     }
19     for (int i = head[u]; i != -1; i = ne[i]) {
20         dfs(e[i]);
21         for (int j = m; j >= v[u]; j--) {
22             for (int k = 0; k <= j - v[u]; k++) {
23                 f[u][j] = max(f[u][j], f[u][j - k] + f[e[i]][k]);
24             }
25         }
26     }
27 }
28 
29 int main() {
30     scanf("%d %d", &n, &m);
31     
32     int root;
33     memset(head, -1, sizeof(head));
34     for (int i = 1; i <= n; i++) {
35         int t;
36         scanf("%d %d %d", v + i, w + i, &t);
37         if (t == -1) root = i;
38         else add(t, i);
39     }
40     
41     dfs(root);
42     printf("%d", f[root][m]);
43     
44     return 0;
45 }
复制代码

 

参考资料

  AcWing 10. 有依赖的背包问题(算法提高课):https://www.acwing.com/video/386/

posted @   onlyblues  阅读(344)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示