經常使用的五大算法,包含 動態規劃、分治法、貪心求解法、回朔法、分支限界法。java
動態規劃(Dynamic Programming),與其說是一種算法,不如說是一種解決問題的思路。 :peach:git
Dynamic Programming is a methed for solving a complex problem by breaking it down into a collection of simpler subproblems.github
上述引自維基百科,也就是說動態規劃就是將一個複雜的問題分解成若干簡單的問題集的一種方法。算法
那麼怎麼分解問題就成了動態規劃的本質。數組
而分解問題,依靠的就是 問題的狀態 和 狀態之間的轉移。bash
咱們須要找到一個問題在某一個狀態的 最優解。 :strawberry:spa
舉個例子:設計
最長遞增子序列(LIS) 給定一個長度爲N的數組,找出一個最長的單調自增子序列(不必定連續,可是順序不能亂) 例如:給定一個長度爲6的數組A{5, 6, 7, 1, 2, 8} 則其最長的單調遞增子序列爲{5,6,7,8},長度爲4code
由此咱們定義一個狀態,當以第i個數組元素結尾的最長遞增子序列dp(i)。 整個數組的LIS就是dp(1)...dp(n)的最大值。字符串
由此咱們就定義好了一個問題的狀態,下面咱們就看看不一樣狀態之間的轉移。
首先,咱們須要找到狀態的 邊界值。 :watermelon:
根據上述LIS的問題,邊界值爲當i=1時,最長遞增子序列爲1。
而後,咱們須要找到狀態之間的 關係。 :banana:
dp(i) = max(1,dp(j)+1...) (0<=j<i) 當array[j]<array[i]
解釋一下,在保證第i項比第j項大的狀況下,要取以前全部項的最長遞增子序列加1的最大值。
這裏能夠看出,這裏的狀態轉移方程,就是定義了問題和子問題之間的關係。 能夠看出,狀態轉移方程就是帶有條件的遞推式。
這裏羅列了6道比較經典的動態規劃練習。 :corn:
/**
* No1.塔樹選擇和最大問題
*
* 一個高度爲N的由正整數組成的三角形,從上走到下,求通過的數字和的最大值。
* 每次只能走到下一層相鄰的數上,例如從第3層的6向下走,只能走到第4層的2或9上。
* 5
* 8 4
* 3 6 9
* 7 2 9 5
* 例子中的最優方案是:5 + 8 + 6 + 9 = 28
*
* 輸入:符合塔樹的二維數組。
* 輸出:通過的最大值。
*/
/**
* No2.∑乘法表問題
*
* ∑ | a b c
* ——————————————
* a | b b a
* b | c b a
* c | a c c
*
* 依此乘法表,對任必定義於∑上的字符串,適當加括號表達式後獲得一個表達式。
* 例如,對於字符串x=bbbba,它的其中一個加括號表達式爲i(b(bb))(ba)。
* 依乘法表,該表達式的值爲a。試設計一個動態規劃算法,對任必定義於∑上的字符串x=x1x2…xn。
* 計算有多少種不一樣的加括號方式,使由x導出的加括號表達式的值爲a。
*
* 輸入:輸入一個以a,b,c組成的任意一個字符串。
* 輸出:計算出的加括號方式數。
*/
/**
* No.3跳臺階
*
* 一隻青蛙一次能夠跳上1級臺階,也能夠跳上2級。
* 求該青蛙跳上一個n級的臺階總共有多少種跳法。
*
* 輸入:臺階數n。
* 輸出:跳法總數。
*/
/**
* No.4最長遞增子序列(LIS)
*
* 給定一個長度爲N的數組,找出一個最長的單調自增子序列(不必定連續,可是順序不能亂)。
* 例如:給定一個長度爲6的數組A{5, 6, 7, 1, 2, 8}。
* 則其最長的單調遞增子序列爲{5,6,7,8},長度爲4。
*
* 輸入:一個數組。
* 輸出:最長遞增子序列的長度。
*/
/**
* No.5揹包問題
*
* 有N件物品和一個容量爲V的揹包。
* 第i件物品的大小是c[i],價值是w[i]。
* 求解將哪些物品裝入揹包可以使價值總和最大。
*
* 輸入:物品大小數組c,物品價值數組w,揹包容量。
* 輸出:最大的價值。
*/
/**
* No.6最長公共子序列(LCS)
*
* 給出兩個字符串a, b,求它們的最長的公共子序列。
*
* 輸入:字符串a和字符串b。
* 輸出:最長的公共子序列的長度。
*/
複製代碼
請先根據動態規劃的本質,定義出 狀態 和 狀態之間的關係,而後再進行代碼編寫。
若是你已經完成了練習,這裏有上述問題的答案,戳這裏。