开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 22 天,点击查看活动详情
有依赖的背包问题
题目描述
有 个物品和一个容量是 的背包。
物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如下图所示:
如果选择物品5,则必须选择物品1和2。这是因为2是5的父节点,1是2的父节点。
每件物品的编号是 ,体积是 ,价值是 ,依赖的父节点编号是 。物品的下标范围是 。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。
输出最大价值。
输入格式
第一行有两个整数 ,,用空格隔开,分别表示物品个数和背包容量。
接下来有 行数据,每行数据表示一个物品。
第 行有三个整数 , , ,用空格隔开,分别表示物品的体积、价值和依赖的物品编号。
如果 ,表示根节点。 数据保证所有物品构成一棵树。
输出格式
输出一个整数,表示最大价值。
数据范围
父节点编号范围:
- 内部结点:;
- 根节点 ;
输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例:
11
题目分析
意如其题,这是一道有依赖的背包问题。
对于一棵有根树求子树的某种特性,我们首先想到的便是用dfs由根向下遍历,但由于有最大体积的限制,使得我们在对子树赋权时无法赋予某一特定体积所对应的价值。
而我们若是依次遍历,复杂度是 复杂度,这是无法接受的。
由于总体积数值较小,便可以以体积作为背包,存子树所有可取体积的价值最大值。
具体实现思路见代码注释。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int h[N], e[N], ne[N];
int v[N], w[N], f[N][N]; // f[i][j] 表示以 i 为根的情况下体积最大为 j 的最大价值
int n, m, root, idx;
void add(int x, int y)
{
e[idx] = y, ne[idx] = h[x], h[x] = idx ++;
}
void dfs(int x)
{
for (int i = v[x]; i <= m; i ++) f[x][i] = w[x];
for (int i = h[x]; i != -1; i = ne[i]) // 枚举以 x 为根的相邻节点
{
int y = e[i];
dfs(y);
for (int j = m; j >= v[x]; j --) // 当前根为视角枚举体积,不可换序
for (int k = j - v[x]; k >= 0; k --) // 以子节点为视角枚举子节点子树体积,可换序
f[x][j] = max(f[x][j], f[x][j - k] + f[y][k]);
}
}
int main()
{
cin >> n >> m;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++)
{
int p;
cin >> v[i] >> w[i] >> p;
if (p != -1) add(p, i);
else root = i;
}
dfs(root);
cout << f[root][m];
return 0;
}