二刷代码随想录训练营Day 34|力扣62.不同路径、63. 不同路径 II、343.整数拆分、96.不同的二叉搜索树

1.不同路径

代码随想录

视频讲解:动态规划中如何初始化很重要!| LeetCode:62.不同路径_哔哩哔哩_bilibili

代码:

cpp 复制代码
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++){
            dp[i][0] = 1;
        }
        for(int j = 0; j < n; j++){
            dp[0][j] = 1;
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
};

note:涉及到方法数,一般就是用加法。这里说只能往右或者往下走,因此就将左方和上方的方法数加起来就是所求。

dp数组的含义:到达(i,j)下标的方法数为dp[i][j]

递推公式:dp[i][j] = dp[i - 1][j] + dp[i][j - 1]

dp数组的初始化:因为需要上方和左方的元素来推导,所以我们要初始化第一行和第一列。由于dp数组的含义是方法数,那么这些初始化的值就都是1,因为只有一种方法------沿直线走。

遍历顺序:由于需要从挨着第一行第一列的元素开始推导,且一个元素的值依赖于它正下方的值和左方的值,因此要用正序遍历

2.不同路径2

代码随想录 (programmercarl.com)

视频讲解:动态规划,这次遇到障碍了| LeetCode:63. 不同路径 II_哔哩哔哩_bilibili

代码:

cpp 复制代码
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        if(obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1){
            return 0;
        }
        vector<vector<int>> dp(m,vector<int>(n,0));
        for(int i = 0; i < m && obstacleGrid[i][0] == 0; i++){
            dp[i][0] = 1;
        }
        for(int j = 0; j < n && obstacleGrid[0][j] == 0; j++){
            dp[0][j] = 1;
        }
        for(int i = 1; i < m; i++){
            for(int j = 1; j < n; j++){
                if(obstacleGrid[i][j] == 1) continue;
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        return dp[m - 1][n - 1];
    }
};

note:这题要注意初始化的时候,如果有障碍,即使跳出循环,障碍以及其以后的元素的方法数都为0,以及在进行递推的时候,当出现障碍的时候,进行continue,跳过本次推导。

3.整数拆分

代码随想录

视频讲解:动态规划,本题关键在于理解递推公式!| LeetCode:343. 整数拆分_哔哩哔哩_bilibili

代码:

cpp 复制代码
class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n + 1);
        dp[0] = 0;
        dp[1] = 0;
        dp[2] = 1;
        for(int i = 3; i <= n; i++){
            for(int j = 1; j <= i / 2; j++){
                dp[i] = max(dp[i],max(j * dp[i - j],j * (i - j)));
            }
        }
        return dp[n];
    }
};

note:这里的核心思想是尽量把n拆成几个近似相等的数相乘。

dp数组的含义:和为i的几个元素的最大乘积为dp[i]

递推公式:i可以拆成两个数或者三个以及三个以上的数------dp[i] = max(dp[i],max(j * (i - j), j * dp( i - j))) 其中的dp[i]也参与了求最大值的过程中,是因为我们要把循环里的历史数也要加入其中找出最大值。

dp数组的初始化:dp[0] = 0 dp[1] = 0,是因为这两个本来就没法拆。没什么意义,为了不影响最大值的求得,直接初始化为0. dp[2] = 1,这个是按照定义来的。

遍历顺序:后面的元素依赖于前面元素的值------从左往右遍历

4.不同的二叉搜索树

代码随想录

视频讲解:动态规划找到子状态之间的关系很重要!| LeetCode:96.不同的二叉搜索树_哔哩哔哩_bilibili

代码:

cpp 复制代码
class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n + 1,0);
        dp[0] = 1;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= i; j++){
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
};

note:

dp数组的含义:有i个节点构成的二叉搜索树有dp[i]种

递推公式:dp[i] += dp[j - 1] * dp[i - j] 其实就是左右子树的种类乘积的累加。注意左右子树结点数之和为i -1 因为要留一个结点作为根结点。

dp数组的初始化:由于题上说节点数大于等于1,所以初始化dp[0]即可 dp[0] = 1,空结点也算一种,而且不会影响后面的推导

遍历顺序:后面的元素依赖于前面元素的值------从左往右遍历

相关推荐
xiaoshiguang34 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡4 小时前
【C语言】判断回文
c语言·学习·算法
别NULL4 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇4 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
CYBEREXP20085 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
ZSYP-S5 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos5 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习5 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA6 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法