多重背包问题

dp分析方式与完全背包一致,因此朴素版本写法基本一致

但是优化方式不同,原因如下:

在完全背包中,优化的依据是:

f[i, j] = max(f[i - 1, j], f[i - 1, j - v] + w, f[i - 1, j - 2v] + 2w, ..., f[i - 1, j - kv] + kw)                            ①

此时k的最大取值为 j / v (下取整),此时令 j = j - v;

f[i, j - v] = max(f[i - 1, j - v], f[i - 1, j - 2v] + w, f[i - 1, j - 3v] + 2w, ...., f[i - 1, j - kv] + (k - 1) w)      ②

因为k最大取值即为 j / v(下取整),因此相当于最多选取 k - 1件 i 物品,因此可以带入①式,即完全背包的优化方式。

但是在多重背包中,由于第 i 件物品只有 s[ i ] 件,因此最多可以选取s[ i ]件(假如 j 足够大),如果按照完全背包的优化方式优化,发现最后一项其实是多出来的,在取max的操作中,多出的这一项无法判断是否影响取值结果,所以不能按照完全背包的优化方式优化。

多重背包优化方式:

将 s[ i ] 件物品进行打包,分为1 + 2 + 4 + 8 + ... + 2 ^ k + c 。

这样是可以正确表示出 1 ~ s[ i ] 中的任何一个数的,证明如下:

1, 2 可以表示出 1 ~ 3 的任何一个数的,因此1, 2, 4 可以表示出1 ~ 7 中的任何一个数(相当于在1 ~ 3 的基础上加 4)同理1, 2, 4, 8 可以表示出1 ~ 15中的任何一个数,递推可得 1 + 2 + 4 + ... + 2 ^ k 可以表示出 1 ~ 2 ^ (k + 1) - 1 中的任何一个数,如果此时2 ^ (k + 1) - 1 < s[ i ] < 2^ (k + 2) - 1,c即为s[ i ] - 2 ^ (k + 1) - 1 。

此时将这s[ i ] 件物品打包成若干件物品,每份物品重量和价值即为 v * cnt, w * cnt 。

接下来就可以按0 - 1背包问题的方法解决。时间复杂度 O( N * log N) 

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 25000;

int n, m;
int v[N], w[N];
int f[N];

int main()
{
	cin >> n >> m;
	int cnt = 0;
	for(int i = 1;i <= n;i ++ )
	{
		int a, b, c;
		cin >> a >> b >> c;//处理输入,a为体积,b为价值,c为个数 
		int k = 1;
		while(k <= c)//打包 
		{
			cnt ++;
			v[cnt] = k * a;
			w[cnt] = k * b;
			c -= k;
			k *= 2;
		}
		if(c > 0)
		{
			cnt ++;
			v[cnt] = a * c;
			w[cnt] = b * c;
		}
	}
	
	n = cnt;//此时的物品个数是cnt
	
	for(int i = 1;i <= n;i ++ )//01背包优化版本
	for(int j = m;j >= v[i];j --)
	f[j] = max(f[j], f[j - v[i]] + w[i]);
	
	cout << f[m] << endl;
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值