揹包問題(01揹包,完全揹包,多重揹包)

原文地址爲: 揹包問題(01揹包,完全揹包,多重揹包)

寫在最前面的

近日爲以下瑣事煩身:

差不多要向學院提交項目申請了,本來是想做個多模式的IM系統的,可是跟往屆通過審覈的項目比起來,缺乏創新和研究價值,所以在文檔上要多做手腳,花點心思。

  • 一大堆的作業,每每期中都是這樣。
  • 一直想讀的DirectUI開源代碼一直沒有進展下去。
  • 準備五月底的軟件設計比賽。
  • 魔獸玩的好菜。
  • 空虛寂寞,想找個女友...
揹包問題,經典有揹包九講

01揹包

魔獸插圖 

不死族的巫妖王發工資拉,死亡騎士拿到一張N元的鈔票(記住,只有一張鈔票),爲了防止自己在戰鬥中頻繁的死掉,他決定給自己買一些道具,於是他來到了地精商店前.

死亡騎士:"我要買道具!"

地精商人:"我們這裏有三種道具,血瓶150塊一個,魔法藥200塊一個,無敵藥水350塊一個."

死亡騎士:"好的,給我一個血瓶."

說完他掏出那張N元的大鈔遞給地精商人.

地精商人:"我忘了提醒你了,我們這裏沒有找客人錢的習慣的,多的錢我們都當小費收了的,嘿嘿."

死亡騎士:"......你妹"

死亡騎士想,與其把錢當小費送個他還不如自己多買一點道具,反正以後都要買的,早點買了放在家裏也好,但是要儘量少讓他賺小費.

現在死亡騎士希望你能幫他計算一下,最少他要給地精商人多少小費.

上面就是一個01揹包問題。上面的問題可以描述爲: 

有n個物品,每個物品的重量爲weight[i],每個物品的價值爲value[i]。現在有一個揹包,它所能容納的重量爲total,問:當你面對這麼多有價值的物品時,你的揹包所能帶走的最大價值是多少?

思路:每個物品無非是裝入揹包或者不裝入揹包,那麼就一個一個物品陸續放入揹包中。可以有

tab[i][j] = max(tab[i-1][j-weight[i]]+value[i],tab[i-1][j]) ({i,j|0<i<=n,0<=j<=total})

其中i表示放第i個物品,j表示揹包所容納的重量,那麼tab[i-1][j-weight[i]]+value[i]表示放入第i物品,剛開始接觸會有疑問,tab[i-1][j-weight[i]]這個值,可以這樣理解:tab[i-1][j]爲裝到上一個物品在揹包j容量時的最佳值,那麼如果我要求在j容量的時候放入現在的i物品的價值,那麼是不是要先得到容量爲(j-weight[i])時候的價值,即先得到 tab[i-1][j-weight[i]] 所以 tab[i-1][j-weight[i]]+value[i] 爲放入第i物品的價值; tab[i-1][j] 就是不放入第i個物品。

動態規劃的思維就在這裏體現了,即tab[i-1][j]是tab[i][j]的最優解(我覺得上面的思路講解較容易理解)。

例子:5個物品,(重量,價值)分別爲:(5,12),(4,3),(7,10),(2,3),(6,6)。

揹包容量

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

5物品

0

0

0

0

0

0

6

12

12

15

15

18

22

22

25

25

4物品

0

0

3

3

3

3

3

12

12

15

15

18

22

22

25

25

3物品

0

0

0

0

0

0

0

12

12

15

15

15

22

22

22

22

2物品

0

0

0

0

3

12

12

12

12

15

15

15

15

15

15

15

1物品

0

0

0

0

0

12

12

12

12

12

12

12

12

12

12

12

故有: 

for i=[weight[0],total]
tab[n-1][i] = weight[0]; // n爲物品數量
for i=[1,n)
for j=[weight[i],total]
tab[n-i-1][j] = max(tab[n-i][j-weight[i]]+value[i],tab[n-i][j])
/* print tab[0][total] */

完全揹包

不死族的巫妖王發工資拉,死亡騎士拿到一張N元的鈔票(記住,只有一張鈔票),爲了防止自己在戰鬥中頻繁的死掉,他決定給自己買一些道具,於是他來到了地精商店前.

死亡騎士:"我要買道具!"

地精商人:"我們這裏有三種道具,血瓶150塊無限個,魔法藥200塊無限個,無敵藥水350塊無限個."

死亡騎士:"好的,給我一個血瓶."

說完他掏出那張N元的大鈔遞給地精商人.

地精商人:"我忘了提醒你了,我們這裏沒有找客人錢的習慣的,多的錢我們都當小費收了的,嘿嘿."

死亡騎士:"......你妹"

死亡騎士想,與其把錢當小費送個他還不如自己多買一點道具,反正以後都要買的,早點買了放在家裏也好,但是要儘量少讓他賺小費.

現在死亡騎士希望你能幫他計算一下,最少他要給地精商人多少小費.

上面的魔獸場景描述跟上面的只有小小的差異,就是物品有一個變爲了無限個,這就是完全揹包問題。完全揹包問題可以描述爲:

有n種物品,每種物品有無限個,每個物品的重量爲weight[i],每個物品的價值爲value[i]。現在有一個揹包,它所能容納的重量爲total,問:當你面對這麼多有價值的物品時,你的揹包所能帶走的最大價值是多少?

有了上面01揹包的式子,這題會變的容易理解很多,只是這個式子要有小小的改動。01揹包在二維數組上操作,就是爲了防止一個物品被放入多次的情況。因此一維數組可以滿足完全揹包的問題如下:

tab[j] = max(tab[j-weight[i]]+value[i],tab[j]);({i,j|0<i<=n,0<=j<=total})

根本就是一樣的,只不過物品可以被放多次。

揹包容量

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

i物品

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

X

故有: 

for i=[0,n)
for(j=weight[i]; j<=total; j++)
tab[j] = max(tab[j-weight[i]]+value[i],tab[j])
/* print tab[0][total] */

多重揹包

不死族的巫妖王發工資拉,死亡騎士拿到一張N元的鈔票(記住,只有一張鈔票),爲了防止自己在戰鬥中頻繁的死掉,他決定給自己買一些道具,於是他來到了地精商店前.

死亡騎士:"我要買道具!"

地精商人:"我們這裏有三種道具,血瓶150塊a個,魔法藥200塊b個,無敵藥水350塊c個."

死亡騎士:"好的,給我一個血瓶."

說完他掏出那張N元的大鈔遞給地精商人.

地精商人:"我忘了提醒你了,我們這裏沒有找客人錢的習慣的,多的錢我們都當小費收了的,嘿嘿."

死亡騎士:"......你妹"

死亡騎士想,與其把錢當小費送個他還不如自己多買一點道具,反正以後都要買的,早點買了放在家裏也好,但是要儘量少讓他賺小費.

現在死亡騎士希望你能幫他計算一下,最少他要給地精商人多少小費.

上面的魔獸場景描述跟上面的又有了小小的差異,就是物品有一個變爲了有限個,問題也就變成了多重揹包問題。

有n種物品,每種物品有amount[i]個,每個物品的重量爲weight[i],每個物品的價值爲value[i]。現在有一個揹包,它所能容納的重量爲total,問:當你面對這麼多有價值的物品時,你的揹包所能帶走的最大價值是多少?

多重和完全更接近,多了數量的限制,用一個count[n]計數數組來限制物品i的數量。當放入第i個物品是較優值的時候,count[i]=count[j-weight[i]]+1(j 的含義請查看代碼);這樣做是因爲,放入第i個物品的操作是基於count[j-weight[i]]放入的,所以當count[i-weight[i]]>=amount[i]時,就要阻止放入即便放入第i個物品是較優值。 故有:

for i=[0,n)
/* 將count數組清零 */
for(j=weight[i]; j<=total; j++)
if count[j-weight[i]]<amout[i]
tab[j] = max(tab[j-weight[i]]+value[i],tab[j]);
if tab[j]=tab[j-weight[i]]+value[i] // 決定放入i是較優解
count[i] = count[j-weight[i]] + 1
else if tab[j]=0 // 防止裝第1個物品和裝其他物品的情況
tab[j] = tab[j-1],count[j] = count[j-1]
else count[j] = count[j-1]
/* print tab[0][total] */

總結

總結都在上面了。

 

附件:

揹包問題--01揹包-完全揹包-多重揹包.rar

 

本文完 Sunday, May 06, 2012

搗亂小子 http://daoluanxiaozi.cnblogs.com/


轉載請註明本文地址: 揹包問題(01揹包,完全揹包,多重揹包)