多层背包问题II

题目背景

本题考查多重背包的二进制优化方法。

题目描述

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

第 i 种物品每件体积是 vi,价值是 wi,最多有 si 件。

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

输入格式

第一行两个整数,N,V用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si 用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式

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

输入 #1

4 5
1 2 3
2 4 1
3 4 3
4 5 2 

 输出 #1

10

 

说明/提示

数据范围
0<N≤2000
0<V≤4000
0<vi,wi,si≤2000

此题来源于洛谷U299430

做这道题,我们需要用到01背包降维,完全背包降维,多重背包降维三个知识点,在这里简单复习一下

首先是01背包
顾名思义,有两种取法,取或不去

 经典例题:洛谷P1048 [NOIP2005 普及组] 采药

	for (int i = 1; i <= n; i++) {
		for (int j = m; j >= w[i]; j--) {
			if (w[i] > j) f[j]=f[j];
			else {
				f[j]=max(f[j],f[j-w[i]]+v[i]);
			}
		}
	}

 完全背包
每个物品有无限个,可以取多个

经典例题:洛谷U200606 完全背包问题

	for (int i = 1; i <= n; i++) {
		for (int j = w[i]; j <= m; j++) {
			if (w[i] > j) f[j]=f[j];
			else {
				f[j]=max(f[j],f[j-w[i]]+v[i]);
			}
		}
	}
多重背包

一个物品最多能取s个

经典例题:洛谷U200694 庆功会

	for(int i=1;i<=n;i++){
		for(int j=m;j>=1;j--){
			for(int k=0;k<=s[i];k++){
				if(j-k*v[i]>=0) f[j]=max(f[j-k*v[i]]+k*w[i],f[j]);
			}
		}
	}

然后我们进入正题

其实我们可以把他转化为01背包问题,不会?且听我慢慢道来

【转化为01背包问题】

 (重点)

有一种好想好写的基本方法是转化为01背包求解:把第i 种物品换成n[i]件01背包中的物品,则得到了物品数为 Σn[i]的01背包问题,直接求解,复杂度仍然是O(V*Σn[i])。

但是我们期望将它转化为01背包问题之后能够像完全背包 一样降低复杂度。仍然考虑二进制的思想,我们考虑把第 i种物品换成若干件物品,使得原问题中第i种物品可取的 每种策略——取0..n[i]件——均能等价于取若干件代换以 后的物品。另外,取超过n[i]件的策略必不能出现。

方法是:将第i种物品分成若干件物品,其中每件物品有一个 系数,这件物品的费用和价值均是原来的费用和价值乘以 这个系数。使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1, 且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13, 就将这种物品分成系数分别为1,2,4,6的四件物品。 分成的 这几件物品的系数和为n[i],表明不可能取多于n[i]件的第 i种物品。

另外这种方法也能保证对于0..n[i]间的每一个整 数,均可以用若干个系数的和表示,这个证明可以分 0..2^k-1和2^k..n[i]两段来分别讨论得出,并不难,希望你 自己思考尝试一下。 这样就将第i种物品分成了O(log n[i])种物品,将原问题转 化为了复杂度为O(V*Σlog n[i])的01背包问题,是很大的改进。

下面给出O(log amount)时间处理一件多重背包中物品的过程,其中amount表示物品的数量:void ZeroOnePack(int cost,int wei

理论成立,那么实践,他来了!!!

void ZeroOnePack(int cost,int weight){
	for(int i=m;i>=cost;i--){
		f[i]=max(f[i],f[i-cost]+weight);
	}
}
void CompletePack(int cost,int weight){
	for(int i=cost;i<=m;i++){
		f[i]=max(f[i],f[i-cost]+weight);
	}
}
void MultiplePack(int cost,int weight,int amount){
	if(cost*amount>=m){
		CompletePack(cost,weight);
		return;
	}//转为完全背包[件数太多,可视为无穷]
	int k=1;//折分的思想
	while(k<amount){
		ZeroOnePack(k*cost,k*weight);//转为01背包
		amount=amount-k;
		k=k*2;
	}
	ZeroOnePack(amount*cost,amount*weight);//转为01背包
}

 (此代码仅供学习参考,严禁抄袭)

 完结撒花!!!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hear the Wind Sing.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值