多重揹包 (1086 51nod)

題目描述:有N種物品,每種物品的數量爲C1,C2......Cn。從中任選若干件放在容量爲W的揹包裏,每種物品的體積爲W1,W2......Wn(Wi爲整數),與之相對應的價值爲P1,P2......Pn(Pi爲整數)。求揹包能夠容納的最大價值。

輸入示例

3 6
2 2 5
3 3 8
1 4 1

輸出示例

9


分析:本題是多重揹包問題,我們顯然可以把每種物品的每一件都作爲一個新的物品按照普通0-1揹包的方法做。
但是0-1揹包的時間複雜度是O(W * N) , 這裏N = C1 + C2 + …+ Cn。

       如果用dp[i][j]表示前i件物品,總重量爲j的時候的最大價值。那麼dp[i][j] = max{dp[i – 1][j – k * Wi] + k * Vi},其中 0 ≤ k ≤ min( j / Wi , Ci),這個的時間複雜度是n * W * max(Ci)。

      其實我們還是應該將問題轉化爲0-1揹包的方法解決,利用二進制的方法。每個數都可以拆分爲2的次方,這樣通過將數量進行2的次方拆分,得到新的重量和價值的物品。

二進制思想:

任何整數都可以表示爲多個二進制數相加。 
假如說現在某種物品有三件,那麼dp的時候,要算三次,分別爲:放一件的時候,放兩件的時候,放三件的時候,,,要三次循環,何不知,在放兩件的時候,可能出現第一件已經放入的情況了,這時候其實是放了三件,等到三個一起放的時候,可能出現已經放過一件或兩件或三件了。。所以一定要用二進制方法 
定理:一個正整數n可以被分解成1,2,4,…,2^(k-1),n-2^k+1(k是滿足n-2^k+1>0的最大整數)的形式,且1~n之內的所有整數均可以唯一表示成1,2,4,…,2^(k-1),n-2^k+1中某幾個數的和的形式。

樣例:

       2 2 5
       3 3 8
      1 4 1
轉化成01後:
       2 2
       4 4
       4 4
       3 3
       6 6
       12 12
       3 3
       1 4

代碼:
      
#include<iostream>
#include<cstdio>
#include<cstring>
#define Max(a,b)a>b?a:b
using namespace std;
int dp[50005];
int weigth[1005],value[1005];
int main()
{
	int n,v;
	scanf("%d%d",&n,&v);
	int sum=0;
	int x,y,z;
	for(int i=0;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		int temp=1;
		while(z>0)
		{
			if(z>=temp)
			{
				weigth[++sum]=temp*x;
				value[sum]=temp*y;
				z=z-temp;
			}
			else
			{
				weigth[++sum]=z*x;
				value[sum]=y*z;
				z=0;
			}
			temp=temp*2;
		}
	}
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=sum;i++)
	{
		for(int j=v;j>=weigth[i];j--)
		{
		//	if(j>=weigth[i])
			   dp[j]=Max(dp[j],dp[j-weigth[i]]+value[i]);
		}
	}
	printf("%d\n",dp[v]);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章