多重背包问题(二进制拆分)

多重背包问题描述:

  有 N N N种物品,第 i i i种物品的体积是 c i c_i ci,价值是 w i w_i wi,每种物品的数量都是有限的,为 n i n_i ni。现有容量为 V V V的背包,求在总体积不超过 V V V的条件下,使得背包的总价值最大。

朴素算法:

  将第 i i i类物品的 n i n_i ni个物品拆分,得 Σ n i \Sigma{n_i} Σni个物品,即将原问题转换为了01背包问题,时间复杂度为 O ( V × Σ n ) O(V\times\Sigma{n}) O(V×Σn)
  也可以在转移的过程中枚举 k k k,表示第 i i i种物品选取的数量。 d p [ i ] [ v ] = m a x ( d p [ i − 1 ] [ v − k ∗ c i ] + k ∗ w i ) , 0 ≤ k ≤ n i dp[i][v]=max(dp[i-1][v-k*c_i]+k*w_i),0\leq{k}\leq{n_i} dp[i][v]=max(dp[i1][vkci]+kwi),0kni
时间复杂度为 O ( V × Σ n ) O(V\times\Sigma{n}) O(V×Σn)

优化(二进制拆分):

二进制拆分:

  一个数 n n n可以拆分为 x x x个数字,分别为
         2 0 , 2 1 , 2 2 , . . . , 2 k − 1 , n − 2 k + 1 , 其中 k 是满足 n − 2 k + 1 > 0 的 最 大 整 数 。 2^0,2^1,2^2,...,2^{k-1},n-2^{k}+1,\text{其中}k\text{是满足}n-2^k+1>0的最大整数。 20,21,22,...,2k1,n2k+1,其中k是满足n2k+1>0
  满足使得这 x x x个数可以组合成任意小于等于 n n n的数。

  由 n − 2 k + 1 > 0 n-2^k+1>0 n2k+1>0,移项,两边同时取数,得 k < l o g ( n + 1 ) k<log(n+1) k<log(n+1),即拆分得的数字个数 x x x = ⌊ l o g ( n + 1 ) ⌋ =\lfloor{log(n+1)}\rfloor =log(n+1)

e . g . e.g. e.g. 7的二进制 7 = 111 分解所得的 001 , 010 , 100 001,010,100 001,010,100这三个数可以组合成任意小于等于7 的数,每种组合都会得到不同的数。15 = 1111 可分解成 0001 , 0010 , 0100 , 1000 0001,0010,0100,1000 0001,0010,0100,1000四个数字,这四个数字进行组合也可以得到1-15之间的任一个数值。

优化:

将第 i i i种物品的 n i n_i ni个物品进行二进制拆分,得到得到拆分后的 x x x个物品,分别为 ( c i , w i ) , ( 2 × c i , 2 × w i ) , ( 4 × c i , 4 × w i ) , . . . , ( 2 x − 1 × c i , 2 x − 1 × w i ) , ( ( n − 2 x + 1 ) × c i , ( n − 2 x + 1 ) × w i ) (c_i,w_i),(2\times c_i,2\times w_i),(4\times c_i,4\times w_i),...,(2^{x-1}\times c_i,2^{x-1}\times w_i),((n-2^x+1)\times c_i,(n-2^x+1)\times w_i) (ci,wi),(2×ci,2×wi),(4×ci,4×wi),...,(2x1×ci,2x1×wi),((n2x+1)×ci,(n2x+1)×wi)
e . g . e.g. e.g. n n n为13时, x = ⌊ l o g ( 13 + 1 ) ⌋ = 3 x=\lfloor{log(13+1)}\rfloor=3 x=log(13+1)=3,故拆分成 1 , 2 , 4 , 6 1,2,4,6 1,2,4,6,根据二进制的性质, 1 ∼ 13 1\sim13 113都可以由 1 , 2 , 4 , 6 1,2,4,6 1,2,4,6这四个数字组合得到。

例题:平分娃娃

在这里插入图片描述

#include<bits/stdc++.h> 
using namespace std;
int m[7];
int w[14*6+1];
int dp[420001];
int main()
{
	int V=0;
	int t=1;
	int j;
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=6;i++){
		cin>>m[i];
		V+=m[i]*i;
		int k=log2(m[i]+1);
		for(j=0;j<=k-1;j++){
			w[t++]=i*pow(2,j);	
		} 
		w[t++]=i*(m[i]-pow(2,k)+1);
	} 
	if(V%2==0){
	for(int i=1;i<=t-1;i++){
		for(int j=V;j>=w[i];j--){
			dp[j]=max(dp[j],dp[j-w[i]]+w[i]);					
		}
	}
	if(dp[V/2]==V/2){
		cout<<"Can be divided.\n";
		}
	else{
		cout<<"Can't be divided.\n";
		}
	}
	else{
		cout<<"Can't be divided.\n";
	}
    return 0;
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
很抱歉,由于我是一名语言模型AI,我并不具备直接运行代码的能力。但是,我可以为您提供一些关于多重背包二进制优化输入c代码的信息和建议。 多重背包问题是一种经典的动态规划问题二进制优化是对传统多重背包算法的一种优化手段,可以大幅提升算法性能。 以下是一个可能的多重背包二进制优化输入c代码的基本框架: ```c #include <stdio.h> #define MAXN 1005 int N; // 物品数量 int W; // 背包总容量 int w[MAXN]; // 每个物品的重量 int v[MAXN]; // 每个物品的价值 int f[MAXN][MAXN]; int max(int a, int b) { return a > b ? a : b; } int main() { scanf("%d %d", &N, &W); for (int i = 1; i <= N; i++) { scanf("%d %d", &w[i], &v[i]); } // 以下为二进制优化部分 int n = 1; for (int i = 1; i <= N; i++) { int k = 1; while (k <= v[i]) { w[n] = k * w[i]; v[n++] = k * v[i]; v[i] -= k; k *= 2; } if (v[i] > 0) { w[n] = v[i] * w[i]; v[n++] = v[i] * v[i]; } } N = n - 1; // 多重背包部分 for (int i = 1; i <= N; i++) { for (int j = W; j >= w[i]; j--) { for (int k = 1; k * w[i] <= j; k++) { f[j] = max(f[j], f[j - k * w[i]] + k * v[i]); } } } printf("%d\n", f[W]); return 0; } ``` 以上代码实现了基于二进制优化的多重背包问题,通过对每个物品按其价值进行二进制分,并将其转化为多个物品,以便于后续的多重背包求解。 需要注意的是,该算法的时间复杂度为 $O(NV\log V)$,其中 $V$ 为所有物品的总价值。如果您需要更高效的算法实现,可以尝试其他优化技巧,如单调队列优化、有界背包优化等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiu_cs

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值