【背包问题】 有依赖的背包问题

本文介绍了一种特殊的背包问题,即物品间存在依赖关系且构成树状结构的背包问题。通过详细的解题思路和两种不同的AC代码实现,展示了如何运用动态规划(DP)解决此类问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

点我进入ACwing官方提交点

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

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

如下图所示:

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

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

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

输出最大价值。

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

接下来有 N 行数据,每行数据表示一个物品。
第 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 1N,V100
1 ≤ v i , w i ≤ 100 1≤v_i,w_i≤100 1vi,wi100
父节点编号范围:

内部结点: 1 ≤ p i ≤ N 1≤p_i≤N 1piN;
根节点 p i p_i pi=−1;

输入样例
5 7
2 3 -1
2 2 1
3 5 1
4 7 2
3 6 2
输出样例:
11

思路讲解

看到一个题目,我们首先需要尝试用暴力的思想去想。
暴力思路:dfs遍历每一个点,对儿子节点进行取或者不取操作。
求最大值,那么我们就会想到使用DP,我们尝试使用DP将暴力进行优化,DP的精髓在于用一个集合代表一整个相关类型,从而达到减少时间复杂度的效果。

注意到,我们的体积是有限的,如果使用DFS的话,也是要求出它儿子节点所用的体积,那么根据这个性质,我们就可以用体积代表儿子节点所在路线的值。也就是以体积作为划分方式。

  • 闫氏DP分析法

集合表示:从前i个儿子节点中选,总体积不超过j且父节点是u的值。(由于依赖前一个点是否被选,所以需要采用递归的方式)
属性: MAX

状态划分:

在这里插入图片描述

根据这个状态划分图,我们就可以知道这只不过是加了一个父节点的分组背包的问题。

这里有两种状态转移方程,一个是闫老师的,另一个是我自己的。
yls是将父节点空间提前腾出来,遍历出结果之后,然后再加上父节点的值。
我的是直接在状态转移方程上面直接加上父节点的值,需要注意的时,父节点只能加一次,从哪一个点转移过来的也是需要仔细分析的。

状态划分图画出来之后不能着急,需要仔细分析所要比对的值。

AC代码(预留父节点位置)

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(x) cout << #x << " = " << x << ENDL
#define mod(x) (x) % MOD
#define ENDL "\n"

typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 100 + 10;
int f[N][N], h[N], ne[N], e[N], idx, w[N], v[N];
int n, m;

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u)
{
	for (int i = h[u]; ~i; i = ne[i])
	{
		int son = e[i];
		dfs(son);
		For(j, m - v[u], 0) _rep(k, 0, j) f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]);
	}
	For(i, m, v[u]) f[u][i] = f[u][i - v[u]] + w[u];
	_for(i, 0, v[u]) f[u][i] = 0;
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> m;

	memset(h, -1, sizeof h);
	int rt = 0;
	_rep(i, 1, n)
	{
		int c;
		cin >> v[i] >> w[i] >> c;
		if (c == -1) rt = i;
		else add(c, i);
	}

	dfs(rt);

	cout << f[rt][m] << ENDL;
	return 0;
}

AC代码(不预留位置)

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(x) cout << #x << " = " << x << ENDL
#define mod(x) (x) % MOD
#define ENDL "\n"

typedef long long ll;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int N = 100 + 10;
int f[N][N], h[N], ne[N], e[N], idx, w[N], v[N];
int n, m;

void add(int a, int b)
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

void dfs(int u)
{

	bool first = true;
	for (int i = h[u]; ~i; i = ne[i])
	{
		int son = e[i];
		dfs(son);
		For(j, m, v[u]) _rep(k, 0, j - v[u]) {
			f[u][j] = max(f[u][j], f[u][j - k] + f[son][k]+ (first ? w[u] : 0));
		}
		if (first) first = false;
	}
	if (h[u] == -1) For(i, m, v[u]) f[u][i] = w[u];
}

int main()
{
#ifdef LOCAL
	freopen("data.in", "r", stdin);
#endif
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);

	cin >> n >> m;

	memset(h, -1, sizeof h);
	int rt = 0;
	_rep(i, 1, n)
	{
		int c;
		cin >> v[i] >> w[i] >> c;
		if (c == -1) rt = i;
		else add(c, i);
	}

	dfs(rt);

	cout << f[rt][m] << ENDL;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值