寫在最前面的
近日為以下瑣事煩身:
差不多要向學院提交項目申請了,本來是想做個多模式的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] */
總結
總結都在上面了。
附件:
本文完 Sunday, May 06, 2012

