背包問題(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/


免責聲明!

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



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