小白學習動態規劃:入門篇

入門動態規劃個人總結

一、動態規劃概念

1. 什麼是動態規劃

​ 動態規劃應用於擁有以下特點的問題:一般需要使用動態規劃時,該問題的解可以由更小的解得出,例如:當求單位爲n的最優解時,可以轉換爲求第n-1個單位的最優解······也就是說問題的解可以根據子問題的解求出。

2. 動態規劃問題的特點

① 問題具有最優子結構性質

如果問題的最優解所包含的子問題的解也是最優的,那麼該問題具有最優子結構

無後效性

簡單理解:某個子問題的解一旦確定則不會被改變,求其它子問題的解時僅會使用到這些已經確定好的解

3.動態規劃的一般思路

① 將原問題分解爲子問題

把原問題分解爲若干個子問題,子問題的規模比原問題更小,但求解的形式是相似的。

子問題的解一旦求出就會被保存,所以每個子問題的解僅需求解一次

② 確定狀態並確定數組(數組維度和長度)

我們往往將子問題的解稱爲一種“狀態”,這種狀態的維度一般在確定狀態後就可以馬上得出,如果這裏你不理解沒有關係,可以看下面的例題,很好理解~

③ 確定初始(邊界)狀態的值

隨着子問題的不斷拆分,最終會到達邊界,而邊界值是可以直接得出的,稱爲動態規劃的初始值或邊界值;

④ 確定狀態轉移方程(遞推公式)

這一步是最重要的!它決定我們能否解決實際問題

找出不同狀態之間(前一個子問題與後一個子問題)的關聯關係,如何根據之前的狀態求出後一個狀態,這也可以理解成遞推公式

例子:

斐波那契數列求第n項的遞推公式:
dp[n]{dp[n1]+dp[n2],if n>2dp[1]=dp[0]=1,if n=1n=0 dp[n] \begin{cases} dp[n-1]+dp[n-2], &if\ n>2 \\ dp[1] = dp[0] = 1, &if\ n=1 \| n=0 \\ \end{cases}
(可選)⑤ 考慮特殊狀態

有些特殊狀態並不是適用於狀態轉移方程中的情況,需要另外拿出來單獨考慮並更新狀態轉移方程,在下面的例題中會遇到這個步驟~

⑥ 編碼實現

還是不太懂或者很複雜?沒關係,結合題目看看就明白了~在你掌握了入門方法後,這六個步驟就會瞭如指掌

二、入門例題講解

類型一、一維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[n]{dp[n1]+dp[n2],if n2dp[0]=1,if n=0dp[1]=2,if n=1 dp[n]\begin{cases}dp[n-1]+dp[n-2], &if\ n≥2\\ dp[0] = 1, &if\ n=0 \\ dp[1] = 2, &if\ n=1 \end{cases}

注意我這裏定義的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]格子的最多路徑就是可以到達它的所有路徑之和
dp[i][j]=dp[i1][j]+dp[i][j1] dp[i][j] = dp[i-1][j] + dp[i][j-1]

所以可以初步得到狀態轉移方程:
dp[i][j]{dp[i1][j]+dp[i][j1],if j1 AND i>=11,if (i=0ANDj=0)OR(i=0 AND j=1)OR(i=1 AND j=0) dp[i][j]\begin{cases}dp[i-1][j]+dp[i][j-1], &if\ j≥1\ AND\ i>=1\\ 1, &if\ (i=0ANDj=0)OR(i=0\ AND\ j=1)OR(i=1\ AND\ j=0)\\ \end{cases}
⑤ 考慮特殊狀態

第一行和第一列的所有格子的最多路徑都只有1種路徑

第一行的格子只能由起點向右走

第一列的格子只能由起點向下走

所以需要更新狀態轉移方程
dp[i][j]{dp[i1][j]+dp[i][j1],if j1 AND i11,if (i=0 AND j0)OR(i0 AND j=0) dp[i][j]\begin{cases}dp[i-1][j]+dp[i][j-1], &if\ j≥1\ AND\ i≥1\\ 1, &if\ (i=0\ AND\ j≥0)OR(i≥0\ AND\ j=0)\\ \end{cases}
⑥ 編碼實現

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) ,那麼起點就等於終點,也就是一種路徑

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章