二刷代码随想录训练营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)下标的方法数为dpij

递推公式:dpij = dpi - 1j + dpij - 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的几个元素的最大乘积为dpi

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

dp数组的初始化:dp0 = 0 dp1 = 0,是因为这两个本来就没法拆。没什么意义,为了不影响最大值的求得,直接初始化为0. dp2 = 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个节点构成的二叉搜索树有dpi种

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

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

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

相关推荐
Pniubi10 小时前
关于算法用现成数据结构和手撕
数据结构
唐墨12312 小时前
linux kernel 比较数据大小的宏定义
linux·算法
拂拉氏12 小时前
【知识讲解-题目讲解】 统计满足K约束的子字符串数量2 (滑动窗口+前缀和+二分查找+分析)
算法·前缀和·二分查找·滑动窗口
通信小呆呆17 小时前
电子战抗干扰技术:当“不被干扰”成为一种系统
算法·通信·mimo·isac
老歌老听老掉牙17 小时前
案例 7:二维 Rosenbrock 函数最小值(香蕉函数)
c++·pso
笑容掩饰我的失落17 小时前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
Severus_black17 小时前
【C++初阶】Stack、Queue算法题大练兵!
c语言·c++·算法·stl
2601_9679791220 小时前
机器学习必修课:经典算法与Python实战
python·算法·机器学习·实战·课程
程序猿打工仔20 小时前
计算机基础(C语言)考编、专技岗
c语言·开发语言·算法
通信小呆呆20 小时前
电子战干扰技术:从“蛮力压制”到“精准欺骗”,再到“认知博弈”
算法·通信·isac