多重背包及其优化

设有 n n n 件物品,背包容量为 m m m ,第 i i i 件物品的体积为 v i v_i vi ,价值为 w i w_i wi ,数量为 s i s_i si ,求背包所能容纳的最大价值
朴素算法:点击链接
直接用 01 01 01 背包的朴素转移方程,时间复杂度为 O ( n m s ) O(nms) O(nms) ,代码如下:

	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= s[i];j++)
			for(int k = m;k >= v[i];k--)
				dp[k] = max(dp[k],dp[k-v[i]]+w[i]);

二进制拆分:点击这里
对物品进行二进制拆分之后再用 01 01 01 背包的转移方程直接转移,时间复杂度为 O ( n m l o g s ) O(nmlogs) O(nmlogs) ,代码如下:

	for(int i = 1;i <= n;i++)
	{
		int base = 1;
		while(s1[i] > base)
		{
			s1[i] -= base;
			v[++cnt] = base*v1[i];
			w[cnt] = base*w1[i];
			base <<= 1;
		}
		if(s1[i])
		{
			v[++cnt] = s1[i]*v1[i];
			w[cnt] = s1[i]*w1[i];
		}
	}
	for(int i = 1;i <= cnt;i++)
		for(int j = m;j >= v[i];j--)
			dp[j] = max(dp[j],dp[j-v[i]]+w[i]);

单调队列优化:点击这里
我们先从最原始的转移方程入手,设 d p [ i ] dp[i] dp[i] 表示容量为 i i i 的背包的最大价值,此状态对于某一件指定物品有转移方程:
d p [ i ] = m a x ( d p [ i ] , d p [ i − v ] + w , d p [ i − 2 v ] + 2 w , . . . , d p [ i − k v ] + k w ) , ( 0 ≤ k ≤ m i n ( s , ⌊ i v ⌋ ) ) dp[i] = max(dp[i],dp[i-v]+w,dp[i-2v]+2w,...,dp[i-kv]+kw),(0\le k \le min(s,\lfloor \frac iv \rfloor)) dp[i]=max(dp[i],dp[iv]+w,dp[i2v]+2w,...,dp[ikv]+kw),(0kmin(s,vi))
i = a + b v i=a+bv i=a+bv ,则有:
d p [ a + b v ] = m a x ( d p [ a + b v ] , d p [ a + ( b − 1 ) v ] + w , . . . , d p [ a + ( b − k ) v ] + k w ) dp[a+bv]=max(dp[a+bv],dp[a+(b-1)v]+w,...,dp[a+(b-k)v]+kw) dp[a+bv]=max(dp[a+bv],dp[a+(b1)v]+w,...,dp[a+(bk)v]+kw)
换元一下,令 k = b − k k=b-k k=bk
= m a x ( d p [ a + k v ] + ( b − k ) w ) , ( 0 ≤ k ≤ m i n ( s , ⌊ i v ⌋ ) =max(dp[a+kv]+(b-k)w),(0\le k \le min(s,\lfloor \frac iv \rfloor) =max(dp[a+kv]+(bk)w),(0kmin(s,vi)
从上个式子我们可以发现 d p [ a + b v ] dp[a+bv] dp[a+bv] 是由 d p [ a + k v ] − k w dp[a+kv]-kw dp[a+kv]kw b w bw bw是常数可忽略对单调性的影响)最大的那个 d p [ a + k v ] dp[a+kv] dp[a+kv] 转移过来的,因此我们可以对每一个余数 a a a 维护一个单调队列来进行状态的转移,因为对于每一个物品每一种体积最多只会进队出队一次,故其时间复杂度为 O ( n m ) O(nm) O(nm) ,代码如下:

	for(int i = 1;i <= n;i++)
	{
		int v = read(),w = read(),s = read();
		memcpy(g,dp,sizeof(dp));
		for(int r = 0;r < v;r++)//枚举余数 
		{
			int head = 0,tail = -1;
			for(int k = 0;r+k*v <= m;k++)
			{
				if(head <= tail && k-qu[head] > s) head++;
				while(head <= tail && g[r+k*v]-k*w >= g[r+qu[tail]*v]-qu[tail]*w) tail--;
				qu[++tail] = k;
				dp[r+k*v] = g[r+qu[head]*v]+(k-qu[head])*w;
			} 
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值