多重揹包問題(二進制拆分)

多重揹包問題描述:

  有NN種物品,第ii種物品的體積是cic_i,價值是wiw_i,每種物品的數量都是有限的,爲nin_i。現有容量爲VV的揹包,求在總體積不超過VV的條件下,使得揹包的總價值最大。

樸素算法:

  將第ii類物品的nin_i個物品拆分,得Σni\Sigma{n_i}個物品,即將原問題轉換爲了01揹包問題,時間複雜度爲O(V×Σn)O(V\times\Sigma{n})
  也可以在轉移的過程中枚舉kk,表示第ii種物品選取的數量。dp[i][v]=max(dp[i1][vkci]+kwi),0knidp[i][v]=max(dp[i-1][v-k*c_i]+k*w_i),0\leq{k}\leq{n_i}
時間複雜度爲O(V×Σn)O(V\times\Sigma{n})

優化(二進制拆分):

二進制拆分:

  一個數nn可以拆分爲xx個數字,分別爲
        20,21,22,...,2k1,n2k+1,其中k是滿足n2k+1>02^0,2^1,2^2,...,2^{k-1},n-2^{k}+1,\text{其中}k\text{是滿足}n-2^k+1>0的最大整數。
  滿足使得這xx個數可以組合成任意小於等於nn的數。

  由n2k+1>0n-2^k+1>0,移項,兩邊同時取數,得k<log(n+1)k<log(n+1),即拆分得的數字個數xx=log(n+1)=\lfloor{log(n+1)}\rfloor

e.g.e.g. 7的二進制 7 = 111 分解所得的 001,010,100001,010,100這三個數可以組合成任意小於等於7 的數,每種組合都會得到不同的數。15 = 1111 可分解成0001,0010,0100,10000001,0010,0100,1000四個數字,這四個數字進行組合也可以得到1-15之間的任一個數值。

優化:

將第ii種物品的nin_i個物品進行二進制拆分,得到得到拆分後的xx個物品,分別爲(ci,wi),(2×ci,2×wi),(4×ci,4×wi),...,(2x1×ci,2x1×wi),((n2x+1)×ci,(n2x+1)×wi)(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)
e.g.e.g.nn爲13時,x=log(13+1)=3x=\lfloor{log(13+1)}\rfloor=3,故拆分成1,2,4,61,2,4,6,根據二進制的性質,1131\sim13都可以由1,2,4,61,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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章