九大背包问题专题--有依赖的背包问题(树形Dp结合)

9.有依赖的背包问题

问题:
有N件物品和一个容量是V的背包。

物品之间具有依赖关系,且依赖关系组成一棵树的形状。如果选择一个物品,则必须选择它的父节点。
如图所示
在这里插入图片描述

如果选择物品5,则必须选择物品1和2,这是因为2是5的父节点,1是2的父节点。
每件物品的编号是i,体积是vi,价值是wi,依赖的父节点编号是pi。物品的下标范围是1…N.

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

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

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

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

数据范围
1<N,V<=100
0<vi,wi<=100
父节点编号范围:
内部节点:1<=pi<=N;
根节点:pi=-1;

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

输出样例
11

分析思路:

背包和树形Dp结合(转化为分组背包问题)

每个结点,把它们对应的子节点都递归计算一下,算出每个子节点不同体积下的最大价值;,每个子节点都是一个物品组,不同体积对应到不同组;整个组里面只能选择一个物品

f[i][j]表示选结点i的情况下,所用的体积是j的情况下,以i为根的整棵子树的最大价值是多少

从上往下递归求解,每做完一个结点,先把它的所有子节点的f[i][j]都算出,每个子节点对应在不同体积下,它们要对应的价值

for(int j=m-v[u];j>=0;j--) //枚举体积 ,(m减当前物品的体积)留一个空位,从大到小(只能选一次) 

若体积大于等于当前物品体积,需要在之前空出的位置,把这个物品加进去
f[u][i-v[u]]+w[u];更新的价值

若体积小于等于当前物品体积,整个子树一个节点都不选择(依赖性)



  for(int k=0;k<=j;k++)  //枚举物品组里面的每个物品 
		      f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]); //更新,看做一维 
	  //每个节点都会有一个f[j],把f[u][j]看做01背包的f 
	  

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N=110;
int n,m; 
int h[N],e[N],ne[N],idx;  
int v[N],w[N],f[N][N];

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!=-1;i=ne[i]){ //先枚举物品组 
		int son=e[i];
		dfs(son); //每个子节点都是一个物品组 
		//这里的物品必须要选择 ,依赖性 
		for(int j=m-v[u];j>=0;j--) //枚举体积 ,留一个空位,从大到小(只能选一次) 
		   for(int k=0;k<=j;k++)  //枚举物品组里面的每个物品 
		      f[u][j]=max(f[u][j],f[u][j-k]+f[son][k]); //更新,看做一维 
	  //每个节点都会有一个f[j],把f[u][j]看做01背包的f 
	}
	for(int i=m;i>=v[u];i--)
	 f[u][i-v[u]]+w[u];
	 for(int i=0;i<v[u];i++)
	  f[u][i]=0;
}
int main(){
	memset(h,-1,sizeof h);
	cin>>n>>m;
	int root;
	for(int i=1;i<=n;i++)
	{
		int p;
		cin>>v[i]>>w[i]>>p;
		if(p==-1)
		root=i;
		else add(p,i); 
	}
	dfs(root);
	cout<<f[root][m]<<endl; //初始化的时候把所有体积都初始化为0,表示体积最多是m的情况下,最大价值为多少 
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值