本人博文《背包問題---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。
故可得偽代碼如下:
- F[0][0] ← 1
- for i ← 1 to N
- do for j ← 0 to V
- if (j < C[i])
- then F[i][j] ← F[i-1][j]
- else
- F[i][j] ← F[i-1][j]+F[i-1][j-C[i]]
- return F[N][V]
上述代碼的空間復雜度為O(NV),由狀態方程可知,F[i][]只與F[i-1][]的狀態有關,故可以用一維數組來代替二維數組,以降低空間復雜度為O(V)。
降低空間復雜度為O(V)的偽代碼如下:
- F[0] ← 1
- for i ← 1 to N
- do for j ← V to C[i]
- if (j >= C[i])
- then F[j] ← F[j]+F[j-C[i]]
- 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]狀態時把背包裝滿的方案,且兩者互斥。
偽代碼如下:
- F[0][0] ← 1
- for i ← 1 to N
- do for j ← 0 to V
- if (j < C[i])
- then F[i][j] ← F[i-1][j]
- else
- F[i][j] ← F[i-1][j]+F[i][j-C[i]]
- return F[N][V]
同樣上述偽代碼的空間復雜度為O(NV),我們也可以通過用一維數組來降低空間復雜度為O(V)。
偽代碼如下:
- F[0] ← 1
- for i ← 1 to N
- do for j ← C[i] to V
- if (j >= C[i])
- then F[j] ← F[j]+F[j-C[i]]
- 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)
- int Package02_FullOfPackage(int Weight[], int nLen, int nCapacity)
- {
- int** MethodTable = NULL;
- CreateTwoDimArray(MethodTable,nLen+1,nCapacity+1);
- MethodTable[0][0] = 1;
- for(int i = 1; i <= nLen; 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][j-Weight[i-1]];
- }
- }
- cout << "MethodTable:" << endl;
- PrintTowDimArray(MethodTable,nLen+1,nCapacity+1);
- int nRet = MethodTable[nLen][nCapacity];
- DestroyTwoDimArray(MethodTable,nLen+1);
- return nRet;
- }
//時間復雜度O(NV)空間復雜度O(V)
- int Package02_FullOfPackage_Compress(int Weight[], int nLen, int nCapacity)
- {
- int * MethodTable = new int [nCapacity+1];
- memset(MethodTable,0,(nCapacity+1)*sizeof(int));
- //initiallize all MethodTable[0] with 1
- MethodTable[0] = 1;
- for(int i = 0; i < nLen; i++)
- {
- for(int j = Weight[i]; j <= nCapacity; j++)
- {
- if(j >= Weight[i])
- MethodTable[j] += MethodTable[j-Weight[i]];
- }
- }
- int nRet = MethodTable[nCapacity];
- delete [] MethodTable;
- return nRet;
- }
//測試代碼
- int main()
- {
- int Weight[] = {1,2,5};
- //int Weight[] = {2,2,2};
- int nCapacity = 20;
- cout << "AllCount:" << Package02_FullOfPackage(Weight,sizeof(Weight)/sizeof(int),nCapacity) << endl;
- cout << "AllCount:" << Package02_FullOfPackage_Compress(Weight,sizeof(Weight)/sizeof(int),nCapacity) << endl;
- return 0;
- }
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
