動態規劃之01揹包,完全揹包,多重揹包

2021-08-06 04:34:47 字數 3922 閱讀 5472

01揹包問題,是用來介紹動態規劃演算法最經典的例子。

f[i,j]表示在前i件物品中選擇若干件放在承重為 j 的揹包中,可以取得的最大價值。

pi表示第i件物品的價值。

決策:為了揹包中物品總價值最大化,第 i件物品應該放入揹包中嗎 ?(0/1的選擇)

題目描述:

有編號分別為a,b,c,d,e的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重為10的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?

name

weight

value12

3456

78910

a260

6699

1212

151515b

2303

3669

991011c6

5000

6666

61011d

5400

0666

661010e4

6000

6666

666只要你能通過找規律手工填寫出上面這張表就算理解了01揹包的動態規劃演算法。

首先要明確這張表是自上向下,從左到右生成的。(也可以從上往下,只是為了便於觀察,理解)

為了敘述方便,用e2單元格表示e行2列的單元格,這個單元格的意義是用來表示只有物品e時,有個承重為2的揹包,那麼這個揹包的最大價值是0,因為e物品的重量是4,揹包裝不了。

對於d2單元格,表示只有物品e,d時,承重為2的揹包,所能裝入的最大價值,仍然是0,因為物品e,d都不是這個揹包能裝的。

同理,c2=0,b2=3,a2=6。

對於承重為8的揹包,a8=15,是怎麼得出的呢?

根據01揹包的狀態轉換方程,需要考察兩個值,

乙個是f[i-1,j],對於這個例子來說就是b8的值9,另乙個是f[i-1,j-wi]+pi;

在這裡,

f[i-1,j]表示我有乙個承重為8的揹包,當只有物品b,c,d,e四件可選時,這個揹包能裝入的最大價值

f[i-1,j-wi]表示我有乙個承重為6的揹包(等於當前揹包承重減去物品a的重量),當只有物品b,c,d,e四件可選時,這個揹包能裝入的最大價值

f[i-1,j-wi]就是指單元格b6,值為9,pi指的是a物品的價值,即6

由於f[i-1,j-wi]+pi = 9 + 6 = 15 大於f[i-1,j] = 9,所以物品a應該放入承重為8的揹包

#includeusing namespace std;

#define v 1500

unsigned int f[10][v];//全域性變數,自動初始化為0

unsigned int weight[10];

unsigned int value[10];

#define max(x,y) (x)>(y)?(x):(y)

int main()

for (int i=1; i<=n; i++)

for (int j=1; j<=m; j++)

else

f[i][j]=f[i-1][j];

} cout<

可以進一步優化記憶體使用。上面計算f[i][j]可以看出,在計算f[i][j]時只使用了f[i-1][0……j],沒有使用其他子問題,因此在儲存子問題的解時,只儲存f[i-1]子問題的解即可。這樣可以用兩個一維陣列解決,乙個儲存子問題,乙個儲存正在解決的子問題。

再進一步思考,計算f[i][j]時只使用了f[i-1][0……j],沒有使用f[i-1][j+1]這樣的話,我們先計算j的迴圈時,讓j=m……1,只使用乙個一維陣列即可。

for i=1……n

for j=m……1

f[j]=max(f[j],f[j-weight[i]+value[i])

[cpp] view plain copy

#includeusing namespace std;

#define v 1500

unsigned int f[v];//全域性變數,自動初始化為0

unsigned int weight[10];

unsigned int value[10];

#define max(x,y) (x)>(y)?(x):(y)

int main()

for (int i=1; i<=n; i++)

for (int j=m; j>=1; j--)

} cout《在看完01揹包問題,再來看完全揹包問題:乙個揹包總容量為v,現在有n個物品,第i個 物品體積為weight[i],價值為value[i],每個物品都有無限多件,現在往揹包裡面裝東西,怎麼裝能使揹包的內物品價值最大?

對比一下,看到的區別是,完全揹包問題中,物品有無限多件。往揹包裡面新增物品時,只要當前揹包沒裝滿,可以一直新增。那麼狀態轉移方程為:

f[i+1][j]=max(f[i][j-k*weight[i+1]]+k*value[i+1]),其中0<=k<=v/weight[i+1]

使用記憶體為一維陣列,偽**

for i=1……n

for j=1……m

f[j]=max(f[j],f[j-weight[i]+value[i])

和01揹包問題唯一不同的是j是從1到m。01揹包問題是在前乙個子問題(i-1

種物品)的基礎上來解決當前問題(i

種物品),向i-1種物品時的揹包新增第i種物品;而完全揹包問題是在解決當前問題(i種物品),向i種物品時的揹包新增第i種物品。

**如下:

#includeusing namespace std;

#define v 1500

unsigned int f[v];//全域性變數,自動初始化為0

unsigned int weight[10];

unsigned int value[10];

#define max(x,y) (x)>(y)?(x):(y)

int main()

for (int i=1; i<=n; i++)

for (int j=1; j<=m; j++)

} cout<

多重揹包問題是0-1揹包問題和完全揹包問題的綜合體,可以描述如下:從n種物品向容積為v的揹包裝入,其中每種物品的體積為w,價值為v,數量為k,問裝入的最大價值總和?

我們知道0-1揹包問題是揹包問題的基礎,所以在解決多重揹包問題的時候,要將多重揹包向0-1揹包上進行轉換。在多重揹包問題中,每種物品有k個,可以將每種物品看作k種,這樣就可以使用0-1揹包的演算法。但是,這樣會增加資料的規模。因為該演算法的時間複雜度為o(v*∑ni

=1ki),所以要降低每種物品的數量ki。

九度教程上給出了一種方法,將原數量為k的物品拆分成若干組,每一組可看成一件新的物品,其價值和重量為改組中所有物品的價值重量的總和,每組物品包含的原物品個數分別為:1、2、4,...2^n  (k,k*2,k*2^2,k*2^3...)(k=1)。這樣就將物品數量大大降低,同時通過對這些若干個原物品組合得到的新物品的不同組合,可以得到0到k之間的任意件物品的價值重量和,所以對所有這些新物品做0-1揹包,即可得到多重揹包的解。轉化之後的時間複雜度為o(v*∑ni

=1log2(ki))。

想一想為什麼可以把一種物品的數量轉換為二進位制的這種方式呢?

我們的轉換,是為了減少計算的次數,列舉十分浪費時間,所以我們想怎樣才能節省時間,還能準確的表示出之前的不同數量呢?我們想到了二進位制,1111這就能表達16中情況,所以我們想著把數量轉換成二進位制的表示,我們的k就是表示每一位的權值,這樣的話,我們向上列舉1111....,總會到達這個數,然後我們因為只有1,所以會有剩下的,再另外開出一組來,就好了。想想這些數量的表示,他可以直接對數字進行二進位制的表示,這樣就大大降低了要計算的數量。

揹包九講**

動態規劃 01揹包 完全揹包

有一類動態規劃可解的問題,它可以描述稱為若干有序的階段,且每個階段的狀態只和上乙個階段的狀態有關,一般把這類問題稱為多階段規劃問題。01揹包問題描述如下 有 n 件物品,每件物品的重量為 w i 價值為 c i 現有乙個容量為 v 的揹包,問如何選取物品放入揹包,使得揹包內物品的總價值最大。其中每種...

01揹包 完全揹包 多重揹包

01揹包 zeroonepack 有n件物品和乙個容量為v的揹包,每種物品均只有一件。第i件物品的費用是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。include include includeusing namespace std const int n 1000 10 int ...

01揹包 完全揹包 多重揹包

01揹包 zeroonepack 有n件物品和乙個容量為v的揹包。每種物品均只有一件 第i件物品的費用是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。完全揹包 completepack 有n種物品和乙個容量為v的揹包,每種物品都有無限件可用。第i種物品的費用是c i 價值是w i 求...