二刷代码随想录训练营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,空结点也算一种,而且不会影响后面的推导

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

相关推荐
算AI7 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
我不会编程5558 小时前
Python Cookbook-5.1 对字典排序
开发语言·数据结构·python
懒羊羊大王&8 小时前
模版进阶(沉淀中)
c++
owde8 小时前
顺序容器 -list双向链表
数据结构·c++·链表·list
第404块砖头8 小时前
分享宝藏之List转Markdown
数据结构·list
GalaxyPokemon9 小时前
Muduo网络库实现 [九] - EventLoopThread模块
linux·服务器·c++
W_chuanqi9 小时前
安装 Microsoft Visual C++ Build Tools
开发语言·c++·microsoft
hyshhhh9 小时前
【算法岗面试题】深度学习中如何防止过拟合?
网络·人工智能·深度学习·神经网络·算法·计算机视觉
蒙奇D索大9 小时前
【数据结构】第六章启航:图论入门——从零掌握有向图、无向图与简单图
c语言·数据结构·考研·改行学it
A旧城以西9 小时前
数据结构(JAVA)单向,双向链表
java·开发语言·数据结构·学习·链表·intellij-idea·idea