動態規劃是一個經典而實用的算法,常常在面試題中出現。html
以最著名的刷題網站leetcode爲例,目前有147道動態規劃算法題,佔比約 13% 。前端
其重要性可見一斑~git
這篇文章就來詳細分析一下動態規劃相關知識點。github
綜合維基百科和《算法導論》的說法,能使用動態規劃解決的問題必須包含下面兩個要素:面試
重疊子問題很好理解,遞歸就是解決重疊子問題的一種方式。 簡單理解就是父問題和子問題處理方式一致,可是規模不一樣。 下面來重點須要分析最優優子結構。算法
問題的最優解包含其子問題的最優解。數組
或者說就是經過子問題的解能夠推導出父問題的解。記住是子問題的解而不是子問題自己。緩存
如何理解?舉個例子:bash
給定一個整數數列 [12, 18, 21, 60],求下面兩個問題前端工程師
對於第一個問題就是具備最優子結構,由於將問題(數組)拆分後求解的結果能夠用於問題自己的解。
子數組的偶數個數 + 當前數是否爲偶數 ? 1 : 0 = 數組的偶數個數
第二個問題就不具備最優子結構,將問題(數組)拆分後求得的解不能直接用於問題自己的解。
子數組的最小公倍數 + 數組某個元素 =/=> 當前數組最小公倍數
第一個問題咱們不須要關心子數組的內容,只須要關心子數組的結果便可,而第二個問題咱們須要知道子數組的組成。
固然有專業術語能夠來表述這種情形,叫作「無後效性」。
《算法導論》中給出的通用模式(套路)以下:
雖然我對描述語言進行了精簡,可是看起來仍是有些囉嗦。再用一句話來歸納就是「不斷縮減問題的規模」,記住是縮減規模不是縮減條件。
以爬樓梯問題爲例進行說明:
題目:一我的上樓梯,樓梯有n階臺階,一次能夠上1階或2階。問有多少種上樓梯的方式。
對於這個問題使用動態規劃來考慮,分解的子問題應該是:
爬 n - 1 階樓梯有多少種方式
爬 n - 2 階樓梯有多少種方式
複製代碼
而縮減條件的方式是(這是錯誤的思路)
爬 n 階樓梯每次只爬1階有多少種方式
爬 n 階樓梯每次只爬2階有多少種方式
複製代碼
理論上來講,大多數問題均可以經過(暴力)枚舉來解決。可是一般枚舉是下冊,由於效率過低,沒法在有限的時間或空間內獲得結果。
因此咱們若是將動態規劃算法和枚舉相比,那麼它至少具備兩個優點。
借用知乎答主阮行止的一個例子進行說明。
題目:一個國家的鈔票面額分別是1元、5元、11元,如何用最少的鈔票湊出15元?
先看解答過程:
用dp(n)
函數表示湊出n元時須要的鈔票數量。 那麼n = 15時考慮可能由 n=10 時加一張5元,n=14 時加一張1元,n=4 時加一張11元。用公式表述以下:
dp(15) = min{dp(10) + 1, dp(14) + 1, dp(4) + 1}
加上已知條件:dp(11) = dp(5) = dp(1) = 1,整個推導公式以下圖:
{% asset_img dp15.jpg %}
而後再結合起來理解動態規劃算法的優點。
某公司出售一段長度爲99米可切割的鋼條,已知鋼條的價格爲Pi(i=1,2,3...),鋼條長度均爲整數。長度價格關係以下:
長度i 1 2 3 4 5 6 7 8 9 10
價格Pi 1 5 8 5 10 17 17 20 24 30
複製代碼
求收益最大的切割方案。
參考:
原文連接:tech.gtxlab.com/dp.html 做者信息:朱德龍,人和將來高級前端工程師。