1. 程式人生 > >每天一道LeetCode-----計算從二維陣列的左上角到達右下角的所有路徑數及最短的那條,如果存在障礙物時又是多少

每天一道LeetCode-----計算從二維陣列的左上角到達右下角的所有路徑數及最短的那條,如果存在障礙物時又是多少

Unique Paths

原題連結Unique Paths
這裡寫圖片描述
計算從左上角有多少條不同的路徑可以到達右下角,移動方向只能是向右和向下。

對於每個位置,都有兩種移動的可能,即向右移動和向下移動。可以用深度優先(dfs)解決,同時為了解決重複計算,可以用動態規劃的思想,記錄從dp[i][j]到達右下角有多少條不同的路徑。

除了深度優先之外,可以用迭代法執行動態規劃,這樣做可以減少dp的維度,只需要一維陣列即可,不過要改變dp的含義,假設當前所在位置為(m, n),那麼dp[n]代表從左上角到達當前位置的不同路徑書,原因為

  • 移動方向只能向右和向下,所以只要向下移動一行,就不能再返回上一行。這說明,對於行的記錄是可以忽略的。
  • 只要遍歷每一行的每一列,不斷更新dp[n]直到到達右下角即可,假設當前在第m行的第n列,那麼dp[n]表示從左上角開始移動可以有多少條不同的路徑達到第m行第n列
  • 對於每個位置,它可以從上一行的當前列向下移動到達當前位置,也可以從當前行的上一列向右移動到達當前位置
  • 所以dp[n] = dp[n] + dp[n - 1],dp[n]代表從上一行的第n列到達右下角的不同路徑數,dp[n - 1]代表從當前行的第n-1列到達右下角的不同路徑數
  • 直到遍歷到右下角,即遍歷了所有行所有列,那麼dp[n]就代表從左上角到達右下角的不同路徑數

程式碼如下

class Solution {
public
: int uniquePaths(int m, int n) { /* 到達第0行,第j列的路徑數只有1條 */ vector<int> dp(n, 1); /* 這裡直接忽略了第0行和第0列,原因是到達第0行和第0列的某個位置的路徑都只有1條 */ for(int i = 1; i < m; ++i) { for(int j = 1; j < n; ++j) { /* dp[j]表示從左上角到達第i行第j列的不同路徑數 */
/* 當i為m-1,j為n-1時,就是從左上角到達右下角的不同路徑數 */ dp[j] = dp[j] + dp[j - 1]; } } return dp[n - 1]; } };

Unique Paths II

原題連結Unique Paths II
這裡寫圖片描述
接著上面的問題,如果某些位置有障礙物,那麼有多少條路徑可以到達右下角。
移動的過程中不能到達障礙物所在的位置。

方法和上面一樣,仍然有一維陣列記錄到達某一列的不同路徑,不過需要判斷某個位置是否有障礙物,如果有,那麼dp[n]為0,否則dp[n] = dp[n - 1] + dp[n]

另外,對於dp[0]要特殊處理,不能像上面一樣直接從第一行第一列開始,因為第0行第0列都可能存在障礙物導致dp[n[不是1而是0.

程式碼如下

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if(obstacleGrid.empty())
            return 0;

        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        /* 如果起始位置和目標位置都有障礙物,那麼就不可能到達 */
        if(n == 0 || obstacleGrid[0][0] == 1 || obstacleGrid[m-1][n-1] == 1)
            return 0;

        vector<int> dp(n);
        /* 到達開始位置的路徑為1 */
        dp[0] = 1;
        /* 到達第0行的某個位置的路徑,有障礙物的話後面就都是0,否則是1 */
        for(int i = 1; i < n; ++i)
            dp[i] = (obstacleGrid[0][i] == 1) ? 0 : dp[i - 1];

        for(int i = 1; i < m; ++i)
        {
            /* 如果當前行的第0列是障礙物,那麼到達當前行的第0列的路徑數為0 */
            if(obstacleGrid[i][0] == 1)
                dp[0] = 0;
            for(int j = 1; j < n; ++j)
            {
                dp[j] = obstacleGrid[i][j] == 1 ? 0 : dp[j] + dp[j - 1];
            }
        }
        return dp[n - 1];
    }
};

原題連結Minimum Path Sum
這裡寫圖片描述
和上面的題一樣,只是這回要求是找到路徑長度最短的那一條,路徑長度是路徑上所有數字的和

仍然採用迭代法的動態規劃,不過要改變dp的含義,即dp[n]表示從左上角到達當前位置的最小路徑長,而不在是不同路徑數

另外,第一行和第一列不再單純的是1,而是數字累加,代表路徑長
程式碼如下

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m = grid.size();
        if(m == 0) { return 0; }
        int n = grid[0].size();
        if(n == 0) { return 0; }

        vector<int> dp(n, 0);
        dp[0] = grid[0][0];
        /* 這裡第一行的路徑長要累加 */
        for(int i = 1; i < n; ++i)
            dp[i] = dp[i - 1] + grid[0][i];

        for(int i = 1; i < m; ++i)
        {
            /* 第一列也要累加 */
            dp[0] += grid[i][0];
            for(int j = 1; j < n; ++j)
            {
                /* 不再是相加,而是找到較小的那個 */
                dp[j] = std::min(dp[j], dp[j - 1]) + grid[i][j];
            }
        }
        return dp[n - 1];
    }
};

上面三道題主要利用動態規劃的思想,另外通過迭代法簡化動態規劃陣列的維度。