有依赖的背包问题
有依赖的背包问题
有 个物品和一个容量是 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
如果选择物品 ,则必须选择物品 和 。这是因为 是 的父节点, 是 的父节点。
每件物品的编号是 ,体积是 ,价值是 ,依赖的父节点编号是 。物品的下标范围是 。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 ,,用空格隔开,分别表示物品个数和背包容量。
接下来有 行数据,每行数据表示一个物品。
第 行有三个整数 ,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 ,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
数据范围
父节点编号范围:
- 内部结点:;
- 根节点 ;
输入样例
5 7 2 3 -1 2 2 1 3 5 1 4 7 2 3 6 2
输出样例:
11
解题思路
如果要选择第个物品,那么就一定要选择第个物品的根节点。也就是说每个物品都存在依赖关系,每个物品的选择都依赖于其父节点。
对于任意一棵树,如果要从这棵树中选择物品,那么这棵树的根节点是一定要选择的。由根节点的每一个子节点所构成的子树,都是相互独立的,因此我们会自然想到枚举各个子树,求每个子树在不同体积限制下选择物品价值的最大值。
对于一棵树所选择的物品,我们可以根据各个子树来进行划分,假设根节点有个子节点。我们可以把选择的物品根据所属的子树分成个部分。而在每个子树内部还可以继续细分成个部分(是限制的体积),分别是,表示从这棵子树中选择物品的体积最多是多少。
因此问题变成了,一共有棵子树,在每一棵子树中可以选择用多少体积,在这样的情况下求总体积不超过的情况下的最大价值。可以发现这是一个分组背包问题。每一棵子树可以看成是一个分组,而组中的个不同的体积可以看成是种不同的物品,每个组都只能选择一种物品。
定义状态表示在以为根的树中,从前棵子树中选择体积不超过且必选根节点的所有方案的集合,属性是价值的最大值。状态转移方程为
其中表示根节点的子节点,表示的子节点的个数,表示在以为根的子树中选择的物品体积不超过。
AC代码如下,时间复杂度为:
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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16534052.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效