動態規划解決機器人網格路徑問題!


想獲得更好的排版,請移步個人博客: https://pushy.site

無障礙物

題目(原題見 LeetCode - 62. 不同路徑):一個機器人位於一個 m x n 網格的左上角 (起始點在下圖中標記為“Start” )。機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角(在下圖中標記為“Finish”)。問總共有多少條不同的路徑?

示例 1:

輸入: m = 3, n = 2
輸出: 3
解釋:
從左上角開始,總共有 3 條路徑可以到達右下角。

1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

通過觀察我們我們得出以下兩個特點:

  1. 機器人所在的正東方向和正南方向,只能有一條路徑可以走,即直走
  2. 到某個點的路徑 = 到該點上一個單位的點路徑 + 到該點左一個單位的點路徑。

為了驗證第二個特點,我們假設網格是 2 * 2。如下圖所示,從 A 到達 D 點的路徑總和就等於 A 點到達 B 點的路徑 + 到達 C 點的路徑總和

新文檔 2019-09-24 10.49.37.jpg

這樣,我們不難寫出遞歸代碼:

public int solution(int m, int n) {
    if (m <= 0 || n <= 0)
        return 0;
    else if (m == 1 || n == 1)
        return 1;
    
    int result = 0;
    result += rec(m - 1, n);
    result += rec(m, n - 1);
    return result;
}

遞歸代碼雖然簡單,但是和斐波那契數列遞歸實現一樣,會出現重復子樹的情況。例如求 4 * 3 的路徑時,就出現了遞歸計算重復子樹的情況:

新文檔 2019-09-24 10.49.37_2.jpg

和斐波那契一樣,可以用動態規划來優化復雜度,不難寫出狀態轉移方程為:f(i, j) = f(i - 1)(j) + f(i)(j - 1)

public int solution(int m, int n) {
    int[][] dp = new int[n][m];

    for (int i = 0; i < n; i++) { // 第一列
        dp[i][0] = 1;
    }
    for (int i = 0; i < m; i++) { // 第一行
        dp[0][i] = 1;
    }
    for (int i = 1; i < n; i++) {
        for (int j = 1; j < m; j++) {
            dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
    }
    return dp[n - 1][m - 1];
}

有障礙物

現在,保持和無障礙物的題意,在網格中設置了障礙物(機器人無法越過障礙物),求到終點的路徑總和。原題見 LeetCode - 63. 不同路徑 II

雖然增加了障礙物,但是前面總結的兩個特點還是適用的。但是,需要做一些改變:

  • 對於正東和正南的方向路徑,一旦遇到障礙物(1),代表無法通過,則后面的路徑全是 0;
  • 第二個特點僅使用於該點不是障礙物;如果為障礙物,則直接將該點路徑設為 0,代表從該點無法通過。

LeetCode 官方圖解如下:

12.gif

public int uniquePathsWithObstacles(int[][] matrix) {
    if (matrix == null) return 0;

    int n = matrix.length;
    int m = matrix[0].length;

    int[][] dp = new int[n][m];
    dp[0][0] = matrix[0][0] == 0 ? 1 : 0;
    if (dp[0][0] == 0) {
        return 0;
    }

    // 第一列
    for (int i = 1; i < n; i++) {
        if (matrix[i][0] != 1) {
            dp[i][0] = dp[i - 1][0];
        }
    }
    // 第一行
    for (int j = 1; j < m; j++) {
        if (matrix[0][j] != 1) {
            dp[0][j] = dp[0][j - 1];
        }
    }

    for (int i = 1; i < n; i++) {
        for (int j = 1; j < m; j++) {
            if (matrix[i][j] != 1) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
    }
    return dp[n - 1][m - 1];
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM