LeetCode算法練習——動態規劃提高(二)

這一部分,對已經做過的技術類型的dp問題進行總結。

LeetCode62. 不同路徑

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

示例 1:

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

示例 2:

輸入: m = 7, n = 3
輸出: 28

此題是一道典型的計數型動態規劃,針對的是二維空間。如果機器人到達grid[i][j]的位置,由於它只能向右或者向左運動,則他的上一步座標可能爲grid[i - 1][j]或grid[i][j - 1],即上一步只存在兩種可能性,那麼最多路徑呢則爲右移和下移路徑可能性之和。

我們令 dp[i][j] 是到達 i, j 最多路徑,可以很容易得出動態方程:dp[i][j] = dp[i-1][j] + dp[i][j-1],注意,對於第一行dp[0][j],或者第一列dp[i][0],由於都是在邊界,所以只能爲1。

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(i == 0 || j == 0)    dp[i][j] = 1;
                else    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
};

LeetCode劍指 Offer 47. 禮物的最大價值

在一個 m*n 的棋盤的每一格都放有一個禮物,每個禮物都有一定的價值(價值大於 0)。你可以從棋盤的左上角開始拿格子裏的禮物,並每次向右或者向下移動一格、直到到達棋盤的右下角。給定一個棋盤及其上面的禮物的價值,請計算你最多能拿到多少價值的禮物?

示例 1:

輸入: 
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
輸出: 12
解釋: 路徑 1→3→5→2→1 可以拿到最多價值的禮物

此題爲上一題的變形,需要統計所過路徑的最大禮物價值。思路同上,在到達grid[i][j]的位置時,由於它只能向右或者向左運動,則他的上一步座標可能爲grid[i - 1][j]或grid[i][j - 1],即上一步只存在兩種可能性,由於計算最大值,我們則需要取這兩種路徑移動的禮物最大值加上當前位置的禮物價值,作爲該位置移動路徑的禮物最大值。

即得到狀態方程:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j],並將實時更新路徑移動的最大價值,這裏我們需要注意初始化i = 0, j = 0的情況,即第一行和第一列狀態的初始化問題。

class Solution {
public:
    int maxValue(vector<vector<int>>& grid) {
        int row = grid.size();
        int column = grid[0].size();
        int ans = INT_MIN;
        vector<vector<int>> dp(row, vector<int>(column, 0));
        for(int i = 0; i < row; i++){
            for(int j = 0; j < column; j++){
                if(i == 0 && j == 0)    dp[i][j] = grid[i][j];
                else if(i == 0)  dp[i][j] = dp[i][j - 1] + grid[i][j];  //初始橫向移動
                else if(j == 0)  dp[i][j] = dp[i - 1][j] + grid[i][j];  //初始縱向移動
                //上一步是橫向移動和縱向移動得到的最大路徑:max(dp[i - 1][j], dp[i][j - 1])
                else    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
                ans = max(ans, dp[i][j]);
            }
        }
        return ans;
    }
};

LeetCode72. 編輯距離

給你兩個單詞 word1 和 word2,請你計算出將 word1 轉換成 word2 所使用的最少操作數 。你可以對一個單詞進行如下三種操作:

  •     插入一個字符
  •     刪除一個字符
  •     替換一個字符
示例 1:

輸入:word1 = "horse", word2 = "ros"
輸出:3
解釋:
horse -> rorse (將 'h' 替換爲 'r')
rorse -> rose (刪除 'r')
rose -> ros (刪除 'e')

示例 2:

輸入:word1 = "intention", word2 = "execution"
輸出:5
解釋:
intention -> inention (刪除 't')
inention -> enention (將 'i' 替換爲 'e')
enention -> exention (將 'n' 替換爲 'x')
exention -> exection (將 'n' 替換爲 'c')
exection -> execution (插入 'u')

動態規劃思路:

看到這道題,我們很自然知道有3個選擇,可以替換、刪除或者增加字符。但是怎麼利用呢?

將大問題拆分爲小問題,即面對word1的前i個要變爲word2的前j個字符時,我們需要消耗多少步呢?

替換的情況:當word1中的前i-1個就可以變換爲word2中的前j-1個,那麼我們只要根據word1中的第i個是否等於word2中的第j個字符進行判斷,如果相等,那麼dp[i][j]=dp[i - 1][j - 1];否則,dp[i][j]=dp[i - 1][j - 1] + 1,加的1就是我們將word1中第i個字符替換爲word2中第j個的消耗。

刪除的情況:當word1中的前i - 1個就可以變換爲word2中的前j個時,我們需要將word1中的第i個字符刪除,dp[i][j]=dp[i - 1][j]+1

增加的情況:當word1中的前i個可以變換爲word2中的前j - 1個時,我們需要將word1中的第i個字符後面增加一個,dp[i][j]=dp[i][j - 1]+1

所以,我們的dp[i][j]取上列的最小值即可:dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])) + 1

ps:注意,我們的dp數組索引均是從1開始,而word中索引是0開始的,所以word1[i - 1] == word2[j - 1],就是在判斷我們word1中的第i個字符是否等於word2中第j個字符。即:dp[i][j] = min(dp[i][j], dp[i - 1][j - 1])

示例的演示過程如下:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int m = word1.size();
        int n = word2.size();
        // 有一個字符串爲空串
        if (n * m == 0) return n + m;
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        for(int i = 0; i < m + 1; i++)
            dp[i][0] = i;
        for(int j = 0; j < n + 1; j++)
            dp[0][j] = j;
        for(int i = 1; i < m + 1; i++){
            for(int j = 1; j < n + 1; j++){
                int del = dp[i - 1][j];         //增
                int add = dp[i][j - 1];         //刪
                int mdf = dp[i - 1][j - 1];     //改
                dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])) + 1;
                if(word1[i - 1] == word2[j - 1])
                    dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]);
            }
        }
        return dp[m][n];
    }
};

 

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