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

多重背包问题描述:

  有 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 ) kk<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这四个数字组合得到。

例题:平分娃娃

多重背包问题(二进制拆分)_第1张图片

#include 
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;
}

你可能感兴趣的:(动态规划,计蒜客)