近日爲以下瑣事煩身:
差不多要向學院提交項目申請了,本來是想做個多模式的IM系統的,可是跟往屆通過審覈的項目比起來,缺乏創新和研究價值,所以在文檔上要多做手腳,花點心思。
不死族的巫妖王發工資拉,死亡騎士拿到一張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
搗亂小子 http://daoluanxiaozi.cnblogs.com/