代码随想录Day39-41 动态规划 | 509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯、62. 不同路径、63. 不同路径2、343. 整数拆分、96. 不同的二叉搜索树

509. 斐波那契数

动态规划

动态规划中每一个状态是由上一个状态推导出来的:重叠子问题

(贪心:每次选取最大/最小的,与上一个状态无关)

动规五部曲

  1. 确定dp数组及下标的含义
  2. 确定递推公式
  3. 确定dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导状态转移公式

动态规划debug

  • 举例推导状态转移公式
  • 打印dp数组

509. 斐波那契数:

力扣

70. 爬楼梯

力扣

746. 使用最小花费爬楼梯

力扣

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。

class Solution {
public:
    int minCostClimbingStairs(vector& cost) {
        int dp[2];
        dp[0] = cost[0], dp[1] = cost[1];
        int cur;
        for(int i = 2; i < cost.size(); i++){
            cur = min(dp[0], dp[1]) + cost[i];
            dp[0] = dp[1];
            dp[1] = cur;
        }
        return min(dp[0], dp[1]);
    }
};

62. 不同路径

力扣

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。总共有多少条不同的路径?

class Solution {
public:
    int uniquePaths(int m, int n) {
        int* dp = new int[n + 1];
        dp[0] = 0;
        for(int i = 1; i < n + 1; i++){
            dp[i] = 1;
        }
        for(int j = 1; j < m; j++){
            for(int i = 1; i < n + 1; i++){
                dp[i] = dp[i] + dp[i - 1];
            }
        }
        return dp[n];
    }
};

数论方法:组合数  代码随想录

一共m + n - 2步,其中m - 1步向下,n - 1步向右

方法数:C_{m+n-2}^{m-1}

63. 不同路径2

力扣

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

网格中有障碍物,网格中的障碍物和空位置分别用 1 和 0 来表示。从左上角到右下角将会有多少条不同的路径?

class Solution {
public:
    int uniquePathsWithObstacles(vector>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        int dp[101];
        for(int i = 0; i <= n; i++){ // line 0
            dp[i] = 0;
        }
        dp[1] = 1;
        for(int j = 1; j <= m; j++){ // line 1 -> m
            for(int i = 1; i <= n; i++){ // row 1 -> n
                if(obstacleGrid[j - 1][i - 1]) dp[i] = 0;
                else dp[i] = dp[i] + dp[i - 1];
            }
        }
        return dp[n];
    }
};

343. 整数拆分

力扣

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积 。

class Solution {
public:
    int integerBreak(int n) {
        int dp[60]; //dp[i]: max score of split i
        dp[0] = 0, dp[1] = 1;
        for(int i = 2; i <= n; i++){
            dp[i] = 0;
            for(int j = 1; j < i; j++){
                dp[i] = max(dp[i], j * dp[i - j]);
                dp[i] = max(dp[i], j * (i - j));
            }
            cout << i << " " << dp[i] << endl;
        }
        return dp[n];
    }
};

解法:

转化为动态规划

拆分成多个整数  =>  拆分成两个整数,再逐步拆分成多个

如9 = 3 + 3 + 3,可以看做先拆分成9 = 3 + 6, 再拆分6 = 3 + 3,因此9的状态依赖3的状态和6的状态。

dp[i]:整数i拆分成2个或多个整数时,乘积的最大值

每次迭代,j遍历1到i-1,判断拆分成j和i-j时的乘积是否比当前大

  • i-j不拆分:j * (i-j)
  • i-j拆分:j * dp[i-j]

优化:只遍历到i/2(因为尽量拆成相同的数)

解法2:(需要证明)

拆成n个3,如果除不尽保留4

96. 不同的二叉搜索树

力扣

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

class Solution {
public:
    int numTrees(int n) {
        int dp[20]; // tree num of n_node = i
        dp[0] = 1, dp[1] = 1, dp[2] = 2;
        for(int i = 3; i <= n; i++){ // n_node = i
            dp[i] = 0;
            for(int j = 1; j <= i; j++){ // root = j
                dp[i] = dp[i] + dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
};

你可能感兴趣的:(代码随想录,Leetcode,动态规划,leetcode,算法)