这篇文章上次修改于 893 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
  • 最简单的动态规划类型
  • 给定一个序列或网格
  • 需要找到序列中某个/些子序列或网格中欧给你的某条路径

    • 某种性质最大/最小
    • 计数
    • 存在性
  • 动态规划方程 dp[i] 表示以 a[i] 结尾的满足条件的子序列性质,dpi 表示以格子 (i, j) 为结尾的满足条件的路径的性质。

    • 最大值/最小值
    • 个数
    • 是否存在
  • 空间优化:如果 dpi 只依赖当前行和前一行,则可以用滚动数组节省空间。

1 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int row = obstacleGrid.size();
        int col = obstacleGrid[0].size();
        vector<vector<int>> dp(row, vector<int>(col, 0));
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (obstacleGrid[i][j] == 1) {
                    continue;
                } 
                if (i == 0 && j == 0) {
                    dp[i][j] = 1;
                    continue;
                } 
                if (i > 0) {
                    dp[i][j] += dp[i - 1][j];
                }
                if (j > 0) {
                    dp[i][j] += dp[i][j - 1];
                }
            }
        }

        return dp[row - 1][col - 1];
    }
};

2 最长上升连续子序列

给定一个整数数组(下标从 0 到 n-1, n 表示整个数组的规模),请找出该数组中的最长上升连续子序列。(最长上升连续子序列可以定义为从右到左或从左到右的序列。)

输入:[5, 4, 2, 1, 3]
输出:4
解释:
给定 [5, 4, 2, 1, 3],其最长上升连续子序列(LICS)为 [5, 4, 2, 1],返回 4。

class Solution {
public:
    /**
     * @param a: An array of Integer
     * @return: an integer
     */
    int longestIncreasingContinuousSubsequence(vector<int> &a) {
        if (a.size() == 0) return 0;
        int ans = longest(a);
        reverse(a.begin(), a.end());
        ans = max(ans, longest(a));
        return ans;
    }

    int longest(vector<int> &a) {
        // dp(i) = dp(i - 1) + 1 && a[i-1] < a[i]
        int n = a.size();
        int ans = 1;
        vector<int> dp(n, 1);
        for (int i = 1; i < n; i++) {
            if (a[i - 1] < a[i]) {
                dp[i] = dp[i - 1] + 1;
            }
            if (dp[i] > ans) {
                ans = dp[i];
            }
        }
        return ans;
    }
};

使用滚动数组压缩空间:

class Solution {
public:
    /**
     * @param a: An array of Integer
     * @return: an integer
     */
    int longestIncreasingContinuousSubsequence(vector<int> &a) {
        if (a.size() == 0) return 0;
        int ans = longest(a);
        reverse(a.begin(), a.end());
        ans = max(ans, longest(a));
        return ans;
    }

    int longest(vector<int> &a) {
        // dp(i) = dp(i-1) + 1 && a[i-1] < a[i]
        int n = a.size();
        int ans = 1;
        int old = 0;
        int now = 1;
        vector<int> dp(2, 1);
        for (int i = 1; i < n; i++) {
            old = now;
            now = 1 - now;
            if (a[i - 1] < a[i]) {
                dp[now] = dp[old] + 1;
            } else {
                dp[now] = 1;
            }
            if (dp[now] > ans) {
                ans = dp[now];
            }
        }
        return ans;
    }
};

3 最小路径和

给定一个只含非负整数的 m∗n 网格,找到一条从左上角到右下角的可以使数字和最小的路径。

输入:
grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:
7
解释:
路线为: 1 -> 3 -> 1 -> 1 -> 1
class Solution {
public:
    /**
     * @param grid: a list of lists of integers
     * @return: An integer, minimizes the sum of all numbers along its path
     */
    int minPathSum(vector<vector<int>> &grid) {
        int row = grid.size();
        if (row == 0) return 0;
        int col = grid[0].size();
        if (col == 0) return 0;
        vector<vector<int>> dp(row, vector<int>(col));
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (i == 0 && j == 0) {
                    dp[i][j] = grid[i][j];
                    continue;
                } 
                if (i == 0) {
                    dp[i][j] = dp[i][j-1] + grid[i][j];
                    continue;
                } 
                if (j == 0) {
                    dp[i][j] = dp[i-1][j] + grid[i][j];
                    continue;
                } 
                dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[row-1][col-1];
    }
};

滚动数组压缩空间:

class Solution {
public:
    /**
     * @param grid: a list of lists of integers
     * @return: An integer, minimizes the sum of all numbers along its path
     */
    int minPathSum(vector<vector<int>> &grid) {
        int row = grid.size();
        if (row == 0) return 0;
        int col = grid[0].size();
        if (col == 0) return 0;
        vector<vector<int>> dp(2, vector<int>(col));
        int now = 0;
        int old = 0;
        for (int i = 0; i < row; i++) {
            old = now;
            now = 1 - now;
            for (int j = 0; j < col; j++) {
                if (i == 0 && j == 0) {
                    dp[now][j] = grid[i][j];
                    continue;
                } 
                if (i == 0) {
                    dp[now][j] = dp[now][j-1] + grid[i][j];
                    continue;
                } 
                if (j == 0) {
                    dp[now][j] = dp[old][j] + grid[i][j];
                    continue;
                } 
                dp[now][j] = min(dp[old][j], dp[now][j-1]) + grid[i][j];
            }
        }
        return dp[now][col-1];
    }
};

4 炸弹袭击

给定一个二维矩阵, 每一个格子可能是一堵墙 W,或者 一个敌人 E 或者空 0 (数字 '0'),返回你可以用一个炸弹杀死的最大敌人数。炸弹会杀死所有在同一行和同一列没有墙阻隔的敌人。由于墙比较坚固,所以墙不会被摧毁。

输入:
grid =[
     "0E00",
     "E0WE",
     "0E00"
]
输出: 3
解释:
把炸弹放在 (1,1) 能杀3个敌人
class Solution {
public:
    /**
     * @param grid: Given a 2D grid, each cell is either 'W', 'E' or '0'
     * @return: an integer, the maximum enemies you can kill using one bomb
     */
    int maxKilledEnemies(vector<vector<char>> &grid) {
        int row = grid.size();
        if (row == 0) return 0;
        int col = grid[0].size();
        if (col == 0) return 0;
        vector<vector<int>> dp(row, vector<int>(col, 0));
        vector<vector<int>> sum(row, vector<int>(col, 0));
        int ans = 0;

        // up
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == 'W') {
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = grid[i][j] == 'E' ? 1 : 0;
                if (i > 0) {
                    dp[i][j] += dp[i-1][j];
                }
                sum[i][j] += dp[i][j];
            }
        }

        // down
        for (int i = row - 1; i >= 0; i--) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == 'W') {
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = grid[i][j] == 'E' ? 1 : 0;
                if (i + 1 < row) {
                    dp[i][j] += dp[i + 1][j];
                }
                sum[i][j] += dp[i][j];
            }
        }

        // left
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == 'W') {
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = grid[i][j] == 'E' ? 1 : 0;
                if (j > 0) {
                    dp[i][j] += dp[i][j-1];
                }
                sum[i][j] += dp[i][j];
            }
        }

        // right
        for (int i = 0; i < row; i++) {
            for (int j = col -1 ; j >= 0; j--) {
                if (grid[i][j] == 'W') {
                    dp[i][j] = 0;
                    continue;
                }
                dp[i][j] = grid[i][j] == 'E' ? 1 : 0;
                if (j + 1 < col) {
                    dp[i][j] += dp[i][j+1];
                }
                sum[i][j] += dp[i][j];
            }
        }

        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                if (grid[i][j] == '0' && sum[i][j] > ans) {
                    ans = sum[i][j];
                }
            }
        }
        return ans;
    }
};

参考

https://www.bilibili.com/video/BV1nt4y1Y7n7?p=2&spm_id_from=pageDriver