1. 程式人生 > >【演算法基礎】動態規劃的理解

【演算法基礎】動態規劃的理解

本章是個很有趣的問題,也是難倒很多人的問題,同時這又是個會而不難的問題。

動態規劃的核心邏輯是:將問題分解為子問題。在《演算法圖解》這本書裡,深入淺出得講了遞推公式的推演邏輯,但是在關鍵部分,遞推公式部分,並沒給出邏輯。

整個過程好像是,前面一段道路很平緩,走起來很舒適,但是突然一個大臺階擋住了去路,本篇將試圖為這個臺階做一下鋪墊,讓這個過程更加容易理解一些。

主體仍然以書上的例子作為演示素材。

問題定義

揹包問題場景之一,可以盜竊的東西如下:

在這裡插入圖片描述

現在你的揹包容量只有4磅,目標是在容量允許的情況下,偷到最有價值的物品組合。

最直接的想法是遍歷,三個物品,每個物品都有被偷和不被偷的2種選擇,所以總共有 2

3 = 8 2^3 = 8 種選擇。

如果有 n n 個物品,那麼問題的規模就變成了 2

n 2^n 次方,顯然這是不可接受的演算法。

現在看看用動態規劃如何解決。

動態規劃

現在把問題拆解為兩個方向:

  • 增多物品選擇
  • 增大揹包空間

所以這是兩個維度的擴充套件,我們用一個二維的表格來跟蹤問題的解決方案。

每個動態規劃演算法都從一個表格開始。

在這裡插入圖片描述

行為物品種類,列為揹包容量大小。

那麼在第一行的時候,意味著我們只能選擇吉他,吉他的重量是1磅,價值是1500美元

所以在揹包容量為1時,最大價值是1500美元,容量為2,3,4時都是1500美元,因為此時只有一個選擇。

所以第一行填滿了:

在這裡插入圖片描述

現在我們擴大物品選擇選項,即音響也可以納入選擇了。我們此時來填第二行。

這裡我們即可以講講填充時的指導規則,而不是自己在腦海裡遍歷。。我們對選項不多的問題,天然有遍歷的傾向,然後問題擴大時,又因為沒有指導規則,就會手足無措。

現在有音響了,意味著我們可以選擇兩個物品:

  • 吉他:重1磅,價值1500美金
  • 音響:重4磅,價值3000美金

我們現在來填第二行第一格。我們可以這麼想,我們到底有多少選擇呢?

其中揹包空間是1磅重,其實我們就兩個選擇:

  • 不選擇音響
  • 選擇音響

如果不選擇音響的話,我們就可以不看第二行了,直接把條件再綜合一下:

  • 不選擇音響(只有吉他可選)
  • 揹包大小為1

能夠偷到的價值是不是和第一行第一列的數值1500美元一樣?

是的。但這只是第一個選擇。如果是第二個選擇呢?我們確定要選擇音響,但是一算,音響重4磅,裝不下,所以最終答案就只能是1500美元。

這個邏輯理解的話,我們可以直接上遞推公式了:

c e l l [ i ] [ j ] = m a x { c e l l [ i 1 ] [ j ] , + cell[i][j] =max{ \begin{cases} cell[i-1][j], \\ 當前商品價值 + 剩餘空間最大價值 \end{cases} }

這兩個選項就分別對應著:當前的商品選還是不選,如果不選,那麼最大價值就是上面一行且同列的值,同列的原因是揹包容量大小相同。
如果選的話,條件也是當前物品能放進揹包,放不進去就談不上第二種選擇了。現在是能選,選上了以後,該物品的價值是到手了的。但是空間不一定用完,因此,我們再去查上一行,對應剩餘空間能裝的物品價值。

注:這個揹包問題的背景設定是每個物品只有一個。現在的被選了,那麼就回退到上一行來看剩餘空間可裝多少價值的問題。

上面的公式可以進一步寫成:

c e l l [ i ] [ j ] = m a x { c e l l [ i 1 ] [ j ] , + c e l l [ i 1 ] [ j ] cell[i][j] =max{ \begin{cases} cell[i-1][j], \\ 當前商品價值 + cell[i-1][j-當前商品重量] \end{cases} }

公式拿到,我們現在來繼續填充表格:

在這裡插入圖片描述

有了這個表格,我們就可以知道,在三個物品可選,且揹包大小為4時,最大可偷價值為3500。

即,表格一旦達成,問題解答就變成直接查表即可。

假定現在我們增加一個商品,一個手機,重量為1磅,價值為2000美金。我們知道吉他價值1500,重1磅,相比於手機,在1磅的情況下下,偷手機是價效比最好的。

所以我們來看新增一行:

在這裡插入圖片描述

即要填的表格是 c e l l [ 4 ] [ 1 ] , c e l l [ 4 ] [ 2 ] , c e l l [ 4 ] [ 3 ] , c e l l [ 4 ] [ 4 ] cell[4][1], cell[4][2], cell[4][3], cell[4][4]

c e l l [ 4 ] [ 1 ] = m a x { c e l l [ 3 ] [ 1 ] , 2000 + c e l l [ 3 ] [ 1 1 ] } = m a x ( 1500 , 2000 ) = 2000 cell[4][1] = max\{ cell[3][1], 2000 + cell[3][1-1] \} = max(1500, 2000) = 2000

這裡出現一個 c e l l [ 0 ] cell[0] ,我們的下標從1開始,所以這就是越界,越界的都用0來處理,因為實際表達的含義是,沒有剩餘空間的意思。

再看 c e l l [ 4 ] [ 2 ] cell[4][2] .

c e l l [ 4 ] [ 2 ] = m a x { c e l l [ 3 ] [ 2 ] , 2000 + c e l l [ 3 ] [ 2 1 ] } = m a x ( 1500 , 3500 ) = 3500. cell[4][2] = max\{cell[3][2] , 2000 + cell[3][2-1]\} = max(1500, 3500) = 3500.

同理:

c e l l [ 4 ] [ 3 ] = m a x { c e l l [ 3 ] [ 3 ] , 2000 + c e l l [ 3 ] [ 3 1 ] } = m a x ( 2000 , 2000 + 1500 ) = 3500 c e l l [ 4 ] [ 4 ] = m a x { c e l l [ 3 ] [ 4 ] , 2000 + c e l l [ 3 ] [ 4 1 ] } = m a x ( 2000 , 2000 + 2000 ) = 4000 cell[4][3] = max\{cell[3][3] , 2000 + cell[3][3-1]\} = max(2000, 2000 + 1500) = 3500 \\ cell[4][4] = max\{cell[3][4] , 2000 + cell[3][4-1]\} = max(2000, 2000 + 2000) = 4000

所以最終在揹包為4磅容量,可偷四種物品時,最大可偷4000美元的東西。

如果物品是更小粒度呢?

比如增加了一件物品重量是0.5磅,那麼這個表格就得按照這個最小的粒度來劃分。

如果可以偷物品的一部分呢?

比如偷大米,小麥,一袋子太多,不是一袋子偷不偷的問題,而是可不可以拿走一部分。

這個問題動態規劃不可解,但是隔壁的貪心演算法可解:偷價效比最高的,即單價最貴的。

如果子問題之間相互依賴呢?

答案是動態規劃處理不了。動態規劃只處理相互離散的子問題。

END.

參考:

《演算法圖解》第九章