【動態規劃】01揹包問題html
【動態規劃】01揹包問題【續】python
看完前面四篇關於揹包問題的文章,你會發現揹包問題其實也不過如此,並且它們之間有不少類似的地方,本篇文章就來揭開它們面紗,將揹包問題完全搞定。code
先來回顧一下三個揹包問題的定義:htm
01揹包:
有N件物品和一個容量爲V的揹包,第i件物品消耗的容量爲Ci,價值爲Wi,求解放入哪些物品可使得揹包中總價值最大。blog
徹底揹包:
有N種物品和一個容量爲V的揹包,每種物品都有無限件可用,第i件物品消耗的容量爲Ci,價值爲Wi,求解放入哪些物品可使得揹包中總價值最大。資源
多重揹包:
有N種物品和一個容量爲V的揹包,第i種物品最多有Mi件可用,每件物品消耗的容量爲Ci,價值爲Wi,求解入哪些物品可使得揹包中總價值最大。get
三種揹包問題都有一個共同的限制,那就是揹包容量,揹包的容量是有限的,這便限制了物品的選擇,而三種揹包問題的共同目的,即是讓揹包中的物品價值最大。基礎
不一樣的地方在於物品數量的限制,01揹包問題中,每種物品只有一個,對於每種物品而言,便只有選和不選兩個選擇。徹底揹包問題中,每種物品有無限多個,因此可選的範圍要大不少。在多重揹包問題中,每種物品都有各自的數量限制。
三種揹包問題雖然對於物品數量的限制不同,但均可以轉化爲01揹包問題來進行思考。在徹底揹包問題中,雖然每種物品均可以選擇無限個,但因爲揹包容量有限,實際上每種物品能夠選擇的數量也是有限的,那麼將每種物品都看作是 V/Ci 種只有一件的不一樣物品,不就成了01揹包問題嗎?對於多重揹包也是如此,只是每種物品的膨脹數量變成了 min{Mi, V/Ci}。
因此說,01揹包問題是全部揹包問題的基礎,弄懂了01揹包問題後,徹底揹包和多重揹包就沒有什麼難的地方了。
下面咱們來對比一下三種揹包問題的狀態轉移方程,以便更好的理解它們之間的聯繫:
01揹包的狀態轉移方程:
F[i,v] = max{F[i-1,v], F[i-1,v-Ci] + Wi}
徹底揹包的狀態轉移方程:
F[i,v] = max{F[i-1,v-kCi] + kWi | 0 <= kCi <= v}
多重揹包的狀態轉移方程:
F[i,v] = max{F[i-1,v-kCi] + kWi | 0 <= k <= Mi}
把這三個方程放到一塊兒,便能很清晰的看到它們之間的關係了,三種揹包問題都是基於子問題來選取價值最大的一個,只是選擇的範圍不同。
01揹包考慮的是選和不選,全部只須要比較兩種策略的最大值便可,而徹底揹包和多重揹包要考慮的是選幾個的問題。
這樣說也許仍是不夠形象,舉個栗子就能比較好的說明了:
假設揹包容量爲10,有兩個物品可選,價值分別爲:3,2,容量佔用分別爲,4,3。
初始狀態:
01揹包的填表法:
徹底揹包的填表法:
多重揹包的填表法:
假設兩種物品的可選數量分別爲:2,1.
下面再來看看三種揹包問題的一維數組解決方案。
01揹包:
for i <- 1 to N for v <- V to Ci F[v] = max{F[v],F[v-Ci] + Wi}
將其核心部分抽象出來:
def ZeroOneKnapsack(F,C,W) for v <- V to C F[v] = max{F[v],F[v-C] + W}
則01揹包問題能夠表示爲:
for i <- 1 to N ZeroOneKnapsack(F,Ci,Wi)
N表明物品數量,Ci表明第i個物品佔用的容量,V表明揹包總容量,Wi表明第i個物品的價值,下同。
徹底揹包:
for i <- 1 to N for v <- Ci to V F[v] = max{F[v],F[v-Ci] + Wi}
將其核心部分抽象出來:
def CompleteKnapsack(F,C,W) for v <- C to V F[v] = max{F[v],F[v-C] + W}
則徹底揹包問題的解能夠表示爲:
for i <- 1 to N CompleteKnapsack(F,Ci,Wi)
多重揹包:
for i <- 1 to N if v < Ci * Mi F[v] = max{F[v],F[v-Ci] + Wi} else for v <- Ci to V k <- 1 while k < M && v > Ci * k F[v] = max{F[v],F[v-Ci*k] + Wi*k} k++
抽象出核心邏輯:
def MultiKnapsack(F,C,W,M) if C * M >= V CompleteKnapsack(F,C,W) return else k <- 1 while k < M ZeroOneKnapsack(F,KC,KW) k++ return
則多重揹包問題的解能夠表示爲:
for i <- 1 to N MultiKnapsack(F,Ci,Wi,Mi)
Mi 表明第i件物品最多可選數量
如今咱們來考慮一種更爲複雜的狀況,若是可選的物品同時具備上述三種特性,即:有的物品只能選一個,有的物品能夠選擇任意多個,有的物品只能選擇有限多個,那麼此時該如何決策呢?
其實有了上面的總結和抽象,這種混合揹包問題就小菜一碟了。
回顧一下上面的三種揹包問題的抽象解,就會發現他們每次都只會考慮一種物品,區別只在於第i個物品的可選策略。因此對於混合揹包問題,一樣也能夠一個一個物品考慮,若是這個物品是最多選一個,那麼就採用01揹包的解決策略,若是是能夠選擇任意多個,那麼就使用徹底揹包的解決策略,若是隻能選擇有限多個,那麼就使用多重揹包的解決策略。
僞代碼以下:
for i <- 1 to N if 第i件物品屬於01揹包 ZeroOneKnapsack(F,Ci,Wi) else if 第i件物品屬於徹底揹包 CompleteKnapsack(F,Ci,Wi) else if 第i件物品屬於多重揹包 MultiKnapsack(F,Ci,Wi,Mi)
到此爲止,咱們就已經比較完美的解決了三種揹包問題,順便還解決了一下混合揹包問題。雖然條件各不相同,可是解題思路卻很類似,相信通過這一篇文章的總結,你對於揹包問題也會有更好的理解,而且領會到這種抽象問題的好處。
固然,更深層次的揹包問題還有不少,好比二維費用問題,物品依賴問題,鑑於博主學疏才淺,暫時也沒有探索的興趣,因此就不一一進行說明了,有興趣的話能夠自行搜索相關內容。
若是本文對你有幫助,不要吝嗇你的點贊哦。也歡迎關注個人公衆號進行留言交流。
文末再贈送一個小福利,關注公衆號並回復: python電子書大全
便可無套路得到上百本python電子書資源。