LeetCode系列之动态规划专题

1. 动态规划题目概述

https://leetcode-cn.com/tag/dynamic-programming/

B站视频讲解:

https://www.bilibili.com/video/BV18x411V7fm

https://www.bilibili.com/video/BV12W411v7rd

“选和不选”的方法,写出递推式,以便进一步分析。

2. 典型题目

2.1 不同的二叉搜索树

https://leetcode-cn.com/problems/unique-binary-search-trees/

有两种方法:1. 动态规划;2. 纯数学方法(看到这种输入为整数,输出为整数的题目,一般都有纯数学方法。关键看你数学功底了)。

  • 动态规划

递推公式:G(n) = ∑G(i-1) • G(n-i),1 ≤ i ≤ n。

int numTrees(int n) {
    vector<int> G(n + 1, 0);
    G[0] = 1;
    G[1] = 1;

    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            G[i] += G[j - 1] * G[i - j];
        }
    }

    return G[n];
}

vector初始化的部分,值得学习。

时间复杂度O(N2),空间复杂度O(N)。

int numTrees(int n) {
    vector<int> G(n + 1);
    G[0] = 1;
    G[1] = 1;

    for (int i = 2; i <= n; i++) {
        G[i] = getSumOfProduct(G, i);
    }

    return G[n];
}

int getSumOfProduct(const vector<int>& G, int n) {
    int ret = 0;
    for (int i = 1; i <= n; i++) {
        ret += G[i - 1] * G[n - i];
    }
    return ret;
}

和上面代码完全相同,但是我觉得这么写,思路更清晰。

  • 卡塔兰数

int numTrees(int n) {
    int64_t c = 1;
    for (int i = 0; i < n; i++) {
        c = 2 * (2 * i + 1) * c / (i + 2);
    }
    return static_cast<int>(c);
}

注意c的类型必须是int64_t,防止乘法溢出。

时间复杂度O(N),空间复杂度O(1)。

2.2 零钱兑换

https://leetcode-cn.com/problems/coin-change/

int coinChange(vector<int>& coins, int amount) {
    const int MAX = amount + 1;
    vector<int> dp(amount + 1, MAX);
    dp[0] = 0;

    for (int i = 1; i <= amount; i++) {
        for (int coin : coins) {
            if (coin <= i) {
                dp[i] = min(dp[i], dp[i - coin] + 1);
            }
        }
    }

    return dp[amount] > amount ? -1 : dp[amount];
}

几个注意点:

  • MAX不能简单实用INT_MAX,因为后面有dp[i] + 1,导致int溢出。官方解答里给的amount + 1,就很巧妙。
  • 注意判断coin ≤ i

时间复杂度O(SN),空间复杂度O(N)。N是总金额,S是硬币面值数。

2.3 最大正方形

https://leetcode-cn.com/problems/maximal-square/

最关键的,还是找出递推式,官方题解如下:

 以下代码有两点说明:

  • 注意vector的初始化,第一个参数是size,第二个参数是初始值。
  • 重载了一个三参数版本的min函数。
int maximalSquare(vector<vector<char>>& matrix) {
    if (matrix.size() == 0 || matrix[0].size() == 0) {
        return 0;
    }
    int maxSide = 0;
    int rows = matrix.size();
    int columns = matrix[0].size();
    vector<vector<int>> dp(rows, vector<int>(columns, 0));
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < columns; j++) {
            if (matrix[i][j] == '1') {
                if (i == 0 || j == 0) {
                    dp[i][j] = 1;
                } else {
                    dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1;
                }
                maxSide = max(maxSide, dp[i][j]);
            }
        }
    }
    int maxSquare = maxSide * maxSide;
    return maxSquare;
}

int min(int a, int b, int c) {
    return std::min(std::min(a, b), c);
}

时间复杂度O(MN),空间复杂度O(MN)。假设矩阵为M行N列。

类似扩展题目

https://leetcode-cn.com/problems/count-square-submatrices-with-all-ones/

2.4 最小路径和

https://leetcode-cn.com/problems/minimum-path-sum/

int minPathSum(vector<vector<int>>& grid) {
    if (grid.size() == 0 || grid[0].size() == 0) {
        return 0;
    }
    int rows = grid.size();
    int columns = grid[0].size();
    vector<vector<int>> dp(rows, vector<int>(columns));
    
    dp[0][0] = grid[0][0];
    for (int i = 1; i < rows; i++) {
        dp[i][0] = grid[i][0] + dp[i-1][0];
    }
    for (int j = 1; j < columns; j++) {
        dp[0][j] = grid[0][j] + dp[0][j-1];
    }

    for (int i = 1; i < rows; i++) {
        for (int j = 1; j < columns; j++) {
            dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
        }
    }

    return dp[rows-1][columns-1];
}

时间复杂度O(MN),空间复杂度O(MN)。假设矩阵为M行N列。

2.5 最佳买卖股票的时机含冷冻期(股票问题汇总,见文末总结)

https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/

2.6 单词拆分

https://leetcode-cn.com/problems/word-break/

https://leetcode-cn.com/problems/word-break-ii/

2.7 爬楼梯

https://leetcode-cn.com/problems/climbing-stairs/

int climbStairs(int n) {
    int p = 0, q = 1, r = 1;
    for (int i = 1; i < n; i++) {
        p = q;
        q = r;
        r = p + q;
    }
    return r;
}

题目比较简单,所以尽量把初值设置的巧妙一些,减少不必要的卫语句。

时间复杂度O(N),空间复杂度O(1)。

2.8 最大矩形

https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

https://leetcode-cn.com/problems/maximal-rectangle/

2.9 分割等和子集

https://leetcode-cn.com/problems/partition-equal-subset-sum/

2.10 不同路径

https://leetcode-cn.com/problems/unique-paths/

3. 总结

对于动态规划题目,最重要的是找出递归式。然后用记忆化技术,将中间计算结果保存。

股票问题汇总:

  • https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
  • https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
  • https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/
  • https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/
  • https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
  • https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
  • https://leetcode-cn.com/problems/gu-piao-de-zui-da-li-run-lcof/
posted @ 2020-07-14 13:03  不写诗的诗人小安  阅读(231)  评论(0编辑  收藏  举报