入門動態規劃個人總結
一、動態規劃概念
1. 什麼是動態規劃
動態規劃應用於擁有以下特點的問題:一般需要使用動態規劃時,該問題的解可以由更小的解得出,例如:當求單位爲n的最優解時,可以轉換爲求第n-1個單位的最優解······也就是說問題的解可以根據子問題的解求出。
2. 動態規劃問題的特點
① 問題具有最優子結構性質。
如果問題的最優解所包含的子問題的解也是最優的,那麼該問題具有最優子結構
② 無後效性。
簡單理解:某個子問題的解一旦確定則不會被改變,求其它子問題的解時僅會使用到這些已經確定好的解
3.動態規劃的一般思路
① 將原問題分解爲子問題
把原問題分解爲若干個子問題,子問題的規模比原問題更小,但求解的形式是相似的。
子問題的解一旦求出就會被保存,所以每個子問題的解僅需求解一次
② 確定狀態並確定數組(數組維度和長度)
我們往往將子問題的解稱爲一種“狀態”,這種狀態的維度一般在確定狀態後就可以馬上得出,如果這裏你不理解沒有關係,可以看下面的例題,很好理解~
③ 確定初始(邊界)狀態的值
隨着子問題的不斷拆分,最終會到達邊界,而邊界值是可以直接得出的,稱爲動態規劃的初始值或邊界值;
④ 確定狀態轉移方程(遞推公式)
這一步是最重要的!它決定我們能否解決實際問題
找出不同狀態之間(前一個子問題與後一個子問題)的關聯關係,如何根據之前的狀態求出後一個狀態,這也可以理解成遞推公式
例子:
斐波那契數列求第n項的遞推公式:
(可選)⑤ 考慮特殊狀態
有些特殊狀態並不是適用於狀態轉移方程中的情況,需要另外拿出來單獨考慮並更新狀態轉移方程,在下面的例題中會遇到這個步驟~
⑥ 編碼實現
還是不太懂或者很複雜?沒關係,結合題目看看就明白了~在你掌握了入門方法後,這六個步驟就會瞭如指掌
二、入門例題講解
類型一、一維dp數組
LeetCode70. 爬樓梯
問題描述:假設你需要爬樓梯,需要爬n階才能到達樓頂,每次可以爬1或2階,由多少種不同的方法可以爬到樓頂?
① 將原問題分解爲子問題
假設n = 5,求爬上第5階樓梯的最多方法,必定要求出第4階的最多方法,求第4階的最多方法,必定要求出第3階的最多方法······最終求爬上第1階的最多方法
② 確定狀態並確定數組
每一個階梯都對應一個狀態,該狀態表示爬上該樓梯的最多方法數值,所以數組維度爲1,數組長度爲n
③ 邊界狀態
爬上第1階樓梯的方法數爲1,即爬1階;
爬上第2階樓梯的方法數爲2,每次爬1階要爬2次或每次爬2階爬1次
④ 確定狀態轉移方程
如何找規律?
手算(少用):列出前面較爲容易計算的例子,手寫出所有情況,再從中找規律得到狀態轉移方程。
假設n = 5,則經過手算可以得到如下表格
階梯數 1 2 3 4 5 方法數 1 2 3 5 8 思想(多用):第n階可以從第(n-2)階跨上來或第(n-1)階爬上來,顯然,到達第n階樓梯的最多方法數等於爬第(n-2)階的最多方法數加上第(n-1)階的最多方法數
注意我這裏定義的dp數組的下標0位置對應爬上第1階樓梯的最多方法數而不是第0階!
⑥ 編碼實現
class Solution {
public int climbStairs(int n) {
int[] maxSum = new int[n];
for (int i = 0; i < maxSum.length; i++) {
if(i == 0){ //第1階
maxSum[i] = 1;
}else if(i == 1){ //第2階
maxSum[i] = 2;
}else{ //第3階及以上
maxSum[i] = maxSum[i-1]+maxSum[i-2];
}
}
return maxSum[n-1];
}
}
類型二、二維dp數組
LeetCode62. 不同路徑
問題描述:一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記爲“Start” )。
機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記爲“Finish”)。
問總共有多少條不同的路徑?
說明:m 和 n 的值均不超過 100。
① 將原問題拆分爲子問題
根據題意:機器人每次只能向下或向右走一步,求走到 [i, j] 位置的最多路徑解有兩種形式:
(上面格子)[i-1,j] 向下走一步到達[i, j]
(左邊格子)[i, j-1]向右走一步到達[i, j]
所以轉化爲求[i-1, j]和[i, j-1]的最多路徑解······
② 確定狀態並確定數組
每個格子都有到達自己的最多路徑,所以每個到達每個格子的最多路徑就是一個狀態。
數組:由於格子擁有二維空間(座標x,座標y),需要用二維數組存儲每個格子的最多路徑數,[i,j]代表第i行第j列的格子,dp[i][j]代表到達該格子的最多路徑
③邊界狀態
站在原地有一種走法,就是在原地,所以dp[0][0] = 1;
向右走第一步有一種走法,dp[0][1] = 1 (if m > 1 && n > 1)
向下走第一步有一種走法,dp[1][0] = 1 (if m > 1 && n > 1)
④ 確定狀態轉移方程
機器人每次可以向右走一步或向下走一步,所以走到第[i, j]格子的最多路徑就是可以到達它的所有路徑之和
所以可以初步得到狀態轉移方程:
⑤ 考慮特殊狀態
第一行和第一列的所有格子的最多路徑都只有1種路徑
第一行的格子只能由起點向右走
第一列的格子只能由起點向下走
所以需要更新狀態轉移方程
⑥ 編碼實現
package leetcode.動態規劃.中等.不同路徑;
/**
* @author Zeng
* @date 2020/1/20 9:34
*/
public class Solution {
public static int uniquePaths(int m, int n) {
int[][] dp = new int[m][n];
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
//上邊界
if(i == 0 && j >= 0){dp[i][j] = 1;continue;}
//左邊界
if(j == 0 && i >= 0){dp[i][j] = 1;continue;}
//其它情況
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
public static void main(String[] args) {
int i = uniquePaths(7, 3);
System.out.println("最多路徑:"+i);
}
}
/**
* 最多路徑:28
*/
注意事項:
如果只有一個格子,即(m, n) = (1, 1) ,那麼起點就等於終點,也就是一種路徑