多重背包二进制分组优化

本文介绍了一种解决多重背包问题的高效算法,通过将多个相同物品转换为不同组合,减少计算次数,实现从O(anm)到O(nloga)的优化。文章提供了完整的C++代码实现,适用于最大质量限制不超过20000的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tty 的求助 2
【问题描述】
“唐诗”的好朋友**特别喜欢购物,一天,她拉着土豪 tty 去购物。她选中 了 n 种商品,第 i 种商品有库存 ai,购买一件商品 i,tty 会获得“唐诗”的好朋 友的好感度 bi,第 i 件商品的质量为 wi。 由于 tty 是土豪,所以他不用考虑钱不够的问题。但是 tty 的力气不大,所 以他只能提起质量不大于 m 千克的商品。tty 想知道他最多能获得多少好感度。 对于 OI 大神 tty 来说,这样的题目显然很简单,但是他身边没有电脑,所 以他只能再次向同为大神的你求助。
【输入格式】
在输入文件 help.in 中,共有 n+1 行。 第一行为两个数 n,m。 后接 n 行,每行 3 个数,第 i 行为 ai,bi,wi。
【输出格式】
在输出文件 help.out 中,有一个数,为 tty 最多获得的好感度。
【样例输入】
3 10

2 3 4

1 4 3

2 5 3
【样例输出】
14
【数据规模与约定】
测试点 数据规模 1~2 n≤5,m≤100,ai≤5 3~5 n≤100,m≤1000,ai≤100 6~10 n≤100,m≤20000,ai≤5000 对于 100%的数据保证 bi 在 int 范围内,wi≤100

这道题显然是一道多重背包,考虑到数据范围,我们应该对O(anm)的复杂度进行优化。

因为有多个相同的物品,我们可以发现, a个相同的物品可以当成1+2+...+2^k+x,共k+1种不同的物品,其中x小于2^(k+1), 这样可以把 a 次重复计算化简为 loga 次。

然后再用dp求解就行了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
const int MAXM = 20000;
typedef long long LL;
typedef double DB;
inline int get(){
	char c;
	while((c = getchar()) < '0' || c > '9');
	int cnt = c - '0';
	while((c = getchar()) >= '0' && c <= '9') cnt = cnt * 10 + c - '0';
	return cnt;
}
int N,M;
int b[MAXN + 10],w[MAXN +10],a,tot;
int f[MAXM + 10];
int main(){
	#ifdef lwy
		freopen("1.txt","r",stdin);
	#else
		freopen("help.in","r",stdin);
		freopen("help.out","w",stdout);
	#endif
	N = get(); M = get();
	tot = 0;
	for(int i = 1; i <= N; i ++){
		a = get(); b[++tot] = get(); w[tot] = get();
		int b1 = b[tot], w1 = w[tot]; 
		a --;
		int nown = 2;
		while(1){
			if(a >= nown){
				a -= nown;
				b[++tot] = b[tot - 1] * 2;
				w[tot] = w[tot - 1] * 2;
			}
			else{
				b1 = a * b1;
				w1 = a * w1;
				b[++tot] = b1;
				w[tot] = w1;
				break; 
			}
			nown *= 2;
		}
	}
	memset(f,0,sizeof(f));
	for(int i = 1; i <= tot; i ++){
		for(int j = M; j >= w[i]; j --){
			f[j] = max(f[j],f[j - w[i]] + b[i]);
		}
	}
	printf("%d",f[M]);
	return 0;
}





### 使用二进制数据求解多重背包问题 对于多重背包问题,当物品的数量较大时,朴素的动态规划方法的时间复杂度较高,达到 \(O(N * V * S)\)[^2]。为了提高效率,可以采用二进制优化的方法。 #### 思路解析 通过将每种物品拆分成若干个新的物品来减少状态转移中的枚举次数。具体来说,如果某种物品有 \(s\) 件,则可将其分解成数量分别为 \(1, 2, 4,\ldots,2^{k}\),以及剩余部分 \((s-(2^{k+1}-1))\) 的新物品集合,其中 \(2^{k}\) 是不超过 \(s\) 的最大幂次方数[^3]。这样做之后,在处理这些被分割出来的子项时就只需要考虑取或者不取两种情况了。 #### 实现方式 下面给出一段 Python 代码用于演示这种基于二进制分组策略下的多重背包问题解决方案: ```python def binary_optimized_knapsack(weights, values, nums, capacity): n = len(weights) new_weights = [] new_values = [] for i in range(n): k = 1 while nums[i] >= k: new_weights.append(k * weights[i]) new_values.append(k * values[i]) nums[i] -= k k <<= 1 if nums[i] > 0: new_weights.append(nums[i] * weights[i]) new_values.append(nums[i] * values[i]) dp = [0] * (capacity + 1) for i in range(len(new_weights)): wgt = new_weights[i] val = new_values[i] for j in range(capacity, wgt - 1, -1): dp[j] = max(dp[j], dp[j-wgt]+val) return dp[-1] ``` 此函数接收四个参数:`weights`, `values`, 和 `nums` 分别表示各个项目的重量列表、价值列表及其对应的数目;最后一个参数 `capacity` 表示背包的最大承重能力。该算法先按照上述规则对原始项目进行预处理得到一系列的新项目,再利用常规的一维数组形式的记忆化搜索完成最终的结果计算过程[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值