【笔记】二进制拆分

二进制拆分

二进制拆分是对多重背包的一种优化方式,可以极大的优化多重背包的时间。

前置

我们回顾下完全背包问题。

背包容积为 C , 有 n 种物品 , 每种物品有 k[i] 个, 第 i 个物品占用 w[i] 的容积,价值为 v[i] 。问能用背包装进的物品和的最大值。

常规做法是将其转化成01背包来做,将 1 ~ k[i] 个第 i 类物品加入到01背包中。

时间复杂度为:O(C×i=1nk[i])

二进制拆分依旧是将完全背包转化为01背包,不过是将 k[i] 个物品变成 log(k[i]) 个物品加入到01背包中。

时间复杂度为:O(C×i=1nlog(k[i]))

原理

一个数可以被拆分为任意二进制的和。

例如:7=20+21+22

任意一个数都可以表示为几个 2 的多少次方之和的形式。

正常将完全背包转移成01背包时,我们从小到大枚举放j个第 i 类物品会更优,但这样其实是会有重复枚举的。

为什么呢?

例如: 我们先将 1 个物品 i 放入背包,再将 2 个物品 i 放入背包后,实际上就已经表示出只放 3 个物品的情况了。

我们在背包的时候考虑将 x 个第 i 物品放入背包的情况,等同于将 x=20+21+.... 个物品放入背包的情况。

例如:我们将 7 个第 i 类物品放入背包,等同于: 将 20 个物品放入背包,再将 21 个物品放入背包,再将 22 个物品放入背包。

这样我们就可以通过将 k[i] 个物品拆成 log(k[i]) 个物品,从而表示出向背包中放入 [1,k[i] 个物品的情况了。

代码实现

点击查看代码
	for (int i=1;i<=n;++i) {
		v[i]=read(); w[i]=read(); k[i]=read();
		for (int j=1;j<=k[i];j<<=1) {
		    V[++tot]=v[i]*j; W[tot]=w[i]*j;
		    k[i]-=j;
		}
		if (k[i]) {
			V[++tot]=v[i]*k[i]; W[tot]=w[i]*k[i];
		}
	}
	for (int i=1;i<=tot;++i) {
		for (int j=C;j>=V[i];--j) {
			dp[j]=max(dp[j],dp[j-V[i]]+W[i]);
		}
	}

例题:

洛谷P1782 传送门

补充:

若遇到求方案可行性的多重背包,可以用 bitset 将二进制拆分进一步优化。

利用bitset的位移实现快速转移。总时间复杂度会变成 O(C×i=1nlog(k[i])ω)

posted @   int_Hello_world  阅读(326)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示