多重背包的二进制优化

上上期我们讨论了多重背包的朴素做法。

时间复杂度显然很高。所以优化是很必要的。

废话不多说,说一下优化思路。

还是这道题:P1776

我们想一下,我们当时用k从s[i]枚举到0是否必要。

我们其实可以合并一些物品。

所以我们可以只枚举一些物品,通过这些物品互相合并,产生新的物品。比如:10=1+2+4+3

那为什么不拆成1+2+7呢?因为这样你就无法合成出4了。

那怎么枚举呢?这里要用到一点倍增的思想了。

给大家看一下局部代码:

相信你看了代码一定秒懂。

int a,b,s;//a是重量,b是价值,s是数量
cin>>a>>b>>s;
//如何合成这些多余的物体
int k=1;
while(k<=s){
	w[++cnt]=k*a;//合成的重量
	v[cnt]=k*b;//合成的价值
	s-=k;//物品数相应减少
	k*=2;//翻倍
}
if(s){//没办法翻倍了
	w[++cnt]=s*a;
	v[cnt]=s*b;
    //剩下的自动合成
}

这样就OK了。

最后是完整代码:

//由于直接枚举取多少个会TLE
//所以我们进行二进制优化
//思考一下,其实我们没必要枚举0-s
//我们可以不断倍增:1,2,4,8,16,32,64,128...
//取这些数量个物品
//这样,通过不断组合可以组合出所有种类
//比如:10=1+2+4+3
//为什么有个3呢?
//因为正好剩个3了
//靠1,2,4,3可以组成0-10的所有数
//5=1+4 6=2+4 7=3+4 8=3+4+1 9=2+3+4 10=1+2+3+4
//所以在枚举时,我们不断倍增就可以了
//这样可以大大减少枚举量
#include <bits/stdc++.h>
using namespace std;
int w[maxn],v[maxn],dp[maxm];
int main(){
	int n,m;
	cin>>n>>m;
	int cnt=0;//记录新物体数(相当于优化后的n)
	for(int i=1;i<=n;i++){
		int a,b,s;
		//a是重量,b是价值,s是数量
		cin>>a>>b>>s;
		//接下来是重中之重
		//如何合成这些多余的物体
		int k=1;
		while(k<=s){
			w[++cnt]=k*a;//合成的重量
			v[cnt]=k*b;//合成的价值
			s-=k;//物品数相应减少
			k*=2;//翻倍
		}
		if(s){//没办法翻倍了
			w[++cnt]=s*a;
			v[cnt]=s*b;
			//剩下的自动合成
		}
	}
	//0-1背包
	for(int i=1;i<=cnt;i++){
		for(int j=m;j>=w[i];j--)
			dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
	}
	cout<<dp[m]<<endl;
	return 0;
}

友情提醒:请不要Ctrl C+Ctrl V,这段代码有问题

那么还有别的优化方式吗?当然有!

下期我们来讨论一下非常变(简)(单)单调队列优化。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值