背包問題---01背包|完全背包(裝滿背包的方案總數分析及實現)


本人博文背包問題---01背包最優方案總數(原理剖析代碼實現)

背包問題----完全背包(最優方案總數分析及實現)

中分別談過“01背包”和“完全背包”實現最大價值的方案總數,這里我們再討論一下這兩種背包被物品剛好裝滿的方案總數。

        網上各大公司經常出題目:假設現在有1元、2元、5元的紙幣很多張,現在需要20塊錢,你能給多少種找錢方案,這就可以認為是完全背包問題,即背包容量為20,物品體積分別為1、2、5。

        還有公司出題目:給定一個數m,將m拆成不同的自然數的和的形式有多少種方案,這就是典型的01背包問題,背包容量為m,物品件數為k,這里面的k是隱含條件,可以求出來,因為m最多由1+2+…+k得到,由此可以根據m求得物品件數的上限。

 

       現在切入正題,我們先談“01背包”將背包剛好裝滿的方案總數。“完全背包”和“01背包”極為相似,只有極少量代碼變動

 

       01背包裝滿的問題抽象化:

       設背包容量為V,一共N件物品,每件物品體積為C[i],每件物品的價值為W[i],求將背包裝滿的方案總數。

       1) 子問題定義:F[i][j]表示前i件物品中選取若干件物品放入剩余空間為j的背包中剛好把背包裝滿的方案總數。

       2) 根據第i件物品體積和所剩背包容量大小進行決策

                                              (1-1)

       注意初始化條件為F[0][0]=1,即沒有物品放入容量為0的背包剛好放滿的方案數為1。

       故可得偽代碼如下:

[cpp]  view plain copy
  1. F[0][0] ← 1  
  2.   
  3.     for i ← 1 to N  
  4.   
  5.             do for j ← 0 to V  
  6.   
  7.                  if (j < C[i])  
  8.   
  9.                         then F[i][j] ← F[i-1][j]  
  10.   
  11.                  else  
  12.   
  13.                      F[i][j] ← F[i-1][j]+F[i-1][j-C[i]]  
  14.   
  15.     return F[N][V]  

        上述代碼的空間復雜度為O(NV),由狀態方程可知,F[i][]只與F[i-1][]的狀態有關,故可以用一維數組來代替二維數組,以降低空間復雜度為O(V)。

        降低空間復雜度為O(V)的偽代碼如下:

[cpp]  view plain copy
  1. F[0] ← 1  
  2.   
  3.     for i ← 1 to N  
  4.   
  5.             do for j ← V to C[i]  
  6.   
  7.                if (j >= C[i])  
  8.   
  9.                     then F[j] ← F[j]+F[j-C[i]]  
  10.   
  11.     return F[V]  

         注意對V的遍歷變為逆序,至於為什么這樣,請看本人博文背包問題---01背包最優方案總數(原理剖析代碼實現)

        接下來看看“完全背包”到底有哪些變化。看過《背包九講》或者本人博文背包問題----完全背包(詳解|代碼實現|背包具體物品的求解)的讀者應該會能很快想到狀態方程的變形,如下:

                                   (1-2)

        不錯,狀態方程是這樣。F[i-1][j]表示背包中不含第i種物品時把背包裝滿的方案,F[i][j-C[i]]表示至少包含一件第i種物品把背包裝滿的方案總數。所以,當j<C[i]時F[i][j] = F[i-1][j];當j >= C[i]時, F[i][j] = F[i][j-C[i]] + F[i-1][j],為什么是兩者的和,因為F[i][j-C[i]]和F[i-1][j]都是[i][j]狀態時把背包裝滿的方案,且兩者互斥。

       偽代碼如下:

[cpp]  view plain copy
  1. F[0][0] ← 1  
  2.   
  3.     for i ← 1 to N  
  4.   
  5.             do for j ← 0 to V  
  6.   
  7.             if (j < C[i])  
  8.   
  9.                         then F[i][j] ← F[i-1][j]  
  10.   
  11.             else  
  12.   
  13.                       F[i][j] ← F[i-1][j]+F[i][j-C[i]]  
  14.   
  15.     return F[N][V]  

        同樣上述偽代碼的空間復雜度為O(NV),我們也可以通過用一維數組來降低空間復雜度為O(V)

        偽代碼如下:

[cpp]  view plain copy
  1. F[0] ← 1  
  2.   
  3. for i ← 1 to N  
  4.   
  5.             do for j ← C[i] to V  
  6.   
  7.             if (j >= C[i])  
  8.   
  9.                     then F[j] ← F[j]+F[j-C[i]]  
  10.   
  11.     return F[V]  

         注意:上面對V的遍歷為正序,為什么?請參考本人博文背包問題----完全背包(詳解|代碼實現|背包具體物品的求解)》。

 

下面提過兩種這兩種背包的實現代碼

01背包裝滿的方案總數:

//時間復雜度O(NV)空間復雜度O(NV)

#include<iostream>
using namespace std;
#define Size 1111

//int dp[Size];
int MethodTable[Size][Size];
int Max(int x,int y)
{
    return x>y?x:y;
}
int Package01_FullOfPackage(int Weight[], int nLen, int nCapacity)
{

	MethodTable[1][0] = 1;//初始化

	for(int i = 2; i <= nLen+1; i++)
	{
		for(int j = 0; j <= nCapacity; j++)
		{
			if(j < Weight[i-1])
				MethodTable[i][j] = MethodTable[i-1][j];
			else
				MethodTable[i][j] = MethodTable[i-1][j] + MethodTable[i-1][j-Weight[i-1]];
		}
	}

	cout << "MethodTable:" << endl;
	
//	PrintTowDimArray(MethodTable,nLen+1,nCapacity+1);



	return MethodTable[nLen+1][nCapacity];
}
int main()
{
	//int Weight[] = {1,1,1,1,1,1};
	int Weight[Size];
	int nCapacity;//空間
	int n_goods;//數量
	cin>>n_goods>>nCapacity;
	for(int k=1;k<=n_goods;k++)
		cin>>Weight[k];
	
	
	cout << "AllCount:" << Package01_FullOfPackage(Weight,n_goods,nCapacity) << endl;
//	cout << "AllCount:" << Package01_FullOfPackage_Compress(Weight,sizeof(Weight)/sizeof(int),nCapacity) << endl;
	return 0;
}

//時間復雜度O(NV)空間復雜度O(V)

#include<iostream>
using namespace std;
#define Size 1111


int MethodTable[Size];
int Max(int x,int y)
{
    return x>y?x:y;
}
int Package01_FullOfPackage(int Weight[], int nLen, int nCapacity)
{

	MethodTable[0]= 1;//初始化

	for(int i = 1; i <= nLen; i++)
	{
		for(int j = nCapacity; j >=Weight[i];j--)
		{
		if(j >= Weight[i])  
                MethodTable[j] += MethodTable[j-Weight[i]];  
		}
	}

	cout << "MethodTable:" << endl;
	
//	PrintTowDimArray(MethodTable,nLen+1,nCapacity+1);



	return MethodTable[nCapacity];
}
int main()
{
	//int Weight[] = {1,1,1,1,1,1};
	int Weight[Size];
	int nCapacity;//空間
	int n_goods;//數量
	cin>>n_goods>>nCapacity;
	for(int k=1;k<=n_goods;k++)
		cin>>Weight[k];
	
	
	cout << "AllCount:" << Package01_FullOfPackage(Weight,n_goods,nCapacity) << endl;
//	cout << "AllCount:" << Package01_FullOfPackage_Compress(Weight,sizeof(Weight)/sizeof(int),nCapacity) << endl;
	return 0;
}

完全背包裝滿的方案總數:

//時間復雜度O(NV)空間復雜度O(NV)

[cpp]  view plain copy
  1. int Package02_FullOfPackage(int Weight[], int nLen, int nCapacity)  
  2. {  
  3.     int** MethodTable = NULL;  
  4.     CreateTwoDimArray(MethodTable,nLen+1,nCapacity+1);  
  5.   
  6.     MethodTable[0][0] = 1;  
  7.   
  8.     for(int i = 1; i <= nLen; i++)  
  9.     {  
  10.         for(int j = 0; j <= nCapacity; j++)  
  11.         {  
  12.             if(j < Weight[i-1])  
  13.                 MethodTable[i][j] = MethodTable[i-1][j];  
  14.             else  
  15.                 MethodTable[i][j] = MethodTable[i-1][j]+MethodTable[i][j-Weight[i-1]];  
  16.         }  
  17.     }  
  18.   
  19.     cout << "MethodTable:" << endl;  
  20.     PrintTowDimArray(MethodTable,nLen+1,nCapacity+1);  
  21.       
  22.     int nRet = MethodTable[nLen][nCapacity];  
  23.     DestroyTwoDimArray(MethodTable,nLen+1);  
  24.     return nRet;  
  25. }  

//時間復雜度O(NV)空間復雜度O(V)

[cpp]  view plain copy
  1. int Package02_FullOfPackage_Compress(int Weight[], int nLen, int nCapacity)  
  2. {  
  3.     int * MethodTable = new int [nCapacity+1];  
  4.     memset(MethodTable,0,(nCapacity+1)*sizeof(int));  
  5.   
  6.     //initiallize all MethodTable[0] with 1  
  7.     MethodTable[0] = 1;  
  8.   
  9.     for(int i = 0; i < nLen; i++)  
  10.     {  
  11.         for(int j = Weight[i]; j <= nCapacity; j++)  
  12.         {  
  13.             if(j >= Weight[i])  
  14.                 MethodTable[j] += MethodTable[j-Weight[i]];  
  15.         }  
  16.     }  
  17.   
  18.     int nRet = MethodTable[nCapacity];  
  19.     delete [] MethodTable;  
  20.     return nRet;  
  21. }  

//測試代碼 

[cpp]  view plain copy
  1. int main()  
  2. {  
  3.     int Weight[] = {1,2,5};  
  4.     //int Weight[] = {2,2,2};  
  5.     int nCapacity = 20;  
  6.     cout << "AllCount:" << Package02_FullOfPackage(Weight,sizeof(Weight)/sizeof(int),nCapacity) << endl;  
  7.     cout << "AllCount:" << Package02_FullOfPackage_Compress(Weight,sizeof(Weight)/sizeof(int),nCapacity) << endl;  
  8.     return 0;  
  9. }  

版權聲明:本文為博主原創文章,未經博主允許不得轉載。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM