Studying-代码随想录训练营day34| 62.不同路径、63.不同路径II、343.整数拆分、96.不同的二叉搜索树

第34天,动态规划part02,牢记五部曲步骤,编程语言:C++

目录

62.不同路径

63.不同路径II

343.整数拆分 

96.不同的二叉搜索树 

总结


62.不同路径

文档讲解:代码随想录不同路径

视频讲解:手撕不同路径

题目:

学习:本题最直观的是使用图论的深度搜索的方法,来枚举出来有多少种路径,总时间复杂度为O(2^(m + n - 1) - 1),时间复杂度是非常大的,在力扣中是超时的。因此本题可以采取动态规划的方法来降低时间复杂度。

使用动态规划方法,可以从动态五部曲入手。

1.确定dp数组以及下标含义:本题类似于一个二维棋盘,因此我们可以设置一个二维dp数组,dp[i][j],就表示为到达i行j列位置的路径总数。

2.确定递推公式:依据题干我们知道到达i行j列,我们只能从i-1行j列,和i行j-1列到达。因此dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。

3.dp数组的初始化:首先依据题干我们可以知道到达第一行各点的路径数量都为1, 也即一直往右走,到达第一列各点的路径数量同理也都为1,因此我们对第一行和第一列进行初始化。

4.确定遍历顺序,依据递推公式我们可以确定每个点都是从其上方和左方推导而来的,因此我们从左到右一层一层遍历即可。

5.距离推导dp数组:

代码:

//时间复杂度O(m*n)
//空间复杂度O(m*n)
class Solution {
public:
    int uniquePaths(int m, int n) {
        //1.确定dp数组和下标含义
        vector<vector<int>> dp(m, vector<int>(n, 0));//其中dp[i][j]表示到达i行j列共有多少条不同的路径
        //2.确定递推公式
        //由于每次只能向下或者向右,因此dp[i][j] = dp[i-1][j] + dp[i][j-1]
        //3.初始化dp数组,由递推公式可知,我们应该初始化所有i=0和j=0的位置,同时,根据题干我们也能知道这些位置都是1
        for(int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for(int j = 0; j < n; j++) {
            dp[0][j] = 1;
        }
        //4.确定遍历顺序,可知每个点都是从其上方和左方推导而来的,因此我们需要从上至下,从左至右进行遍历
        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];
            }
            //5.打印dp数组
            //cout << dp[i][j] << endl; //调试的时候使用
        }
        return dp[m - 1][n - 1];
    }
};

本题还可以通过数论来进行求解,由题意可知,我们无论如何都是要走m+n-2步才能到达终点的,其中由m-1步是要往下走的,不同路径就取决于这m-1步什么时候走,因此通过排列组合能够推出,总共的取法为Cm+n-2(上标m-1)。

代码:使用数论最关键的是防止两个int相乘出现溢出的情况,因此我们需要在计算分子的时候,不断除以分母。

//时间复杂度O(m)
//空间复杂度O(1)
class Solution {
public:
    int uniquePaths(int m, int n) {
        long long numerator = 1; // 分子
        int denominator = m - 1; // 分母
        int count = m - 1;
        int t = m + n - 2;
        while (count--) {
            numerator *= (t--);
            while (denominator != 0 && numerator % denominator == 0) {
                numerator /= denominator;
                denominator--;
            }
        }
        return numerator;
    }
};

63.不同路径II

文档讲解:代码随想录不同路径II

视频讲解:手撕不同路径

题目:

学习:本题与上一题不同在于存在障碍物,因此我们需要对障碍物进行单独处理。从动态五部曲出发:

1.确定dp数组:与上一题一样,设置二维dp数组,dp[i][j]表示从起始点出发,到达(i,j)的不同路径数量。

2.确定递推公式:递推公式与上一题一样,但是要注意障碍物单独处理,也即有障碍物的地方就不用再进行赋值了(初始为0)

if (obstacleGrid[i][j] == 0) { // 当(i, j)没有障碍的时候,再推导dp[i][j]
    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}

3.dp数组初始化:同样也是第一行,第一列为1,但是要注意如果第一行出现障碍物,或者第一列出现障碍物,后面的格子就到达不了了,就不进行1的赋值了。(因为只能往下和右走,不能绕路)

4.确定遍历顺序,遍历顺序与上一题相同。

代码:

//时间复杂度O(n*m)
//空间复杂度O(m)
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;
        //1.确定dp数组及下标含义
        //dp[i][j]就表示到达i行j列有的路径数量
        vector<vector<int>> dp(m,vector<int>(n, 0));
        //2.确定递推公式
        //dp[i][j] = dp[i - 1][j] + dp[i][j - 1],但是要注意如果obstacleGrid[i][j]处有障碍物,则不进行赋值
        //3.初始化dp数组,也是需要赋值第一行和第一列,不同的在于如果第一行或者第一列上有障碍物,则后面的都无法到达,为0
        for(int i = 0; i < m; i++) {
            if(obstacleGrid[i][0] == 1) break; //遇到障碍物,后面的都无法抵达,保持为0
            dp[i][0] = 1;
        }
        for(int j = 0; j < n; j++) {
            if(obstacleGrid[0][j] == 1) break;
            dp[0][j] = 1;
        }
        //4.确定遍历顺序,从上至下,从左至右
        for(int i = 1; i < m; i++) {
            for(int j = 1; j < n; j++) {
                if(obstacleGrid[i][j] == 0) { //不是障碍物时再进行赋值
                    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
                }
            }
        }
        return dp[m - 1][n - 1];
    }
};

343.整数拆分 

文档讲解:代码随想录整数拆分

视频讲解:手撕整数拆分

题目:

学习:本题难点在于找到递推公式,也即找到数与数之间的关系。以动规五部曲来说。

1.确定dp数组以及下标的含义,依据题意我们可以设置一个一维的dp数组,dp[i]就表示为n = i时,乘积的最大值。

2.确定递推公式:我们需要思考dp[i]的最大值该如何得到,对于比i小的数来说,例如dp[i - 1],dp[i - 2],它们分别表示了对i - 1拆分后乘积能够得到的最大值,以及对i - 2拆分后乘积能够得到的最大值,相较于i,它们之间就差一个差值,也即dp[i] 有可能会是 1*dp[i -1]也有可能会是2*dp[i-2]以此类推,这是获得dp[i]的一个途径。其次我们知道1*dp[i-1]中的dp[i-1]是对i-1进行整数拆分后得到的最大乘积,因此我们还错过了1*(i-1)的可能,虽然一般来说1*dp[i-1]是大于1*(i-1)的,但不保证中间不会有更大的情况出现。综上我们可以得出递推公式为:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});

3.dp数组的初始化:要注意对于0和1来说,实际上是拆分不了的,因为题干要求了,至少拆分为2个数,且n>=2,因此我们这里对dp[2]进行初始化,dp[2]=1;

4.确定遍历顺序:显然我们需要依靠dp[i - j]的状态,所以i一定要从前往后遍历。

5.举例推导dp数组:

代码:

class Solution {
public:
    int integerBreak(int n) {
        //1.确定dp数组以及下标的含义
        vector<int> dp(n + 1); //dp[i]表示n=i时的最大乘积
        //2.确定递推公式:dp[i] = max(dp[i], (i - j) * j, dp[i - j] * j) 对j进行遍历
        //3.初始化dp数组:dp[0]和dp[1]都没有意义
        dp[2] = 1;
        //4.确定遍历顺序,外层需要对i进行从小到达遍历进行赋值,内层对j进行遍历,找到最大值
        for(int i = 3; i < n + 1; i++) {
            for(int j = 1; j <= i - 2; j++) { //最多取到i - 
                dp[i] = max(dp[i], max(j*(i - j), j*dp[i - j]));//max只能同时比较两个数
            } 
        }
        return dp[n];
    }
};

代码:可以对j的范围进行优化,根据数论,对一个数进行拆分,尽可能拆成相同的数最后得到会是最大的,因为a+b >= 2根号(ab),因此要ab最大,就是取等于号的时候,此时a = b。

//时间复杂度O(n^2)
//空间复杂度O(n)
class Solution {
public:
    int integerBreak(int n) {
        //1.确定dp数组以及下标的含义
        vector<int> dp(n + 1); //dp[i]表示n=i时的最大乘积
        //2.确定递推公式:dp[i] = max(dp[i], (i - j) * j, dp[i - j] * j) 对j进行遍历
        //3.初始化dp数组:dp[0]和dp[1]都没有意义
        dp[2] = 1;
        //4.确定遍历顺序,外层需要对i进行从小到达遍历进行赋值,内层对j进行遍历,找到最大值
        for(int i = 3; i < n + 1; i++) {
            for(int j = 1; j <= i/2; j++) { //最多取到i - 
                dp[i] = max(dp[i], max(j*(i - j), j*dp[i - j]));//max只能同时比较两个数
            } 
        }
        return dp[n];
    }
};

96.不同的二叉搜索树 

文档讲解:代码随想录不同的二叉搜索树

视频讲解:手撕不同的二叉搜索树

题目:

学习:本题同样找到递推公式是关键,依据动规五部曲我们进行分析。

1.确定dp数组以及下标含义:在这里我们需要找到给定节点个数,能够得到的二叉搜索树种数。因此我们可以创建一个一维dp数组,dp[i]就表示i个节点能够得到的二叉搜索树种数。

2.确定递推公式:我们从n=1和n=2来看,对于n=1来说显然只有一棵二叉搜索树,n=2则有两棵二叉搜索树。

n=3时,则有5棵二叉搜索树

分析n=3的情况,当1为头节点时,实际上左子树节点数为0,右子树节点数为2,因此右子树共有n=2种可能。当2为头节点时,左子树节点数为1,右子树节点数为1,因此左右子树都是n=1种可能,乘起来就是1种可能。当3为头节点,左子树节点数为2,右子树节点数为0,因此左子树共有n=2种可能,右子树只有1种可能,乘起来就是2种可能。以此我们可以得出一个公式:dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]。由此我们可以推出:dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]。

所以递推公式:dp[i] += dp[j - 1] * dp[i - j]; ,j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量。

3.dp数组初始化:空节点实际上也可以作为一棵树包括空节点,左子树为空,右子树为空的情况,因此需要进行初始化dp[0] = 1,对于1个节点也能够通过dp[0]推出。

4.确定遍历顺序:从递归公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为i的状态是依靠 i之前节点数的状态。

5.举例推导dp数组:

代码:

//时间复杂度O(n^2)
//空间复杂度O(n)
class Solution {
public:
    int numTrees(int n) {
        //1.确定dp数组以及下标含义
        vector<int> dp(n + 1); //dp[i] 表示i个节点能够组成的二叉搜索树的种类
        //2.确定递推公式:dp[i] += dp[j - 1] * dp[i - j] //对j进行遍历
        //3.初始化dp数组:由于空节点实际上也是一颗二叉树,,因此需要初始化
        dp[0] = 1;
        //dp[1] = 1; //可以进行初始化,也可以通过dp[0]推导而来
        //4.确定遍历顺序,从小到大进行遍历
        for(int i = 1; i < n + 1; i++) {
            for(int j = 1; j <= i; j++) {
                dp[i] += dp[j - 1]*dp[i - j];
            }
        }
        return dp[n];
    }
};

总结

做动态规划一定要牢记,动规五部曲。推导递推公式时,最重要的是从简单的往复杂的推,逐一分析找到关系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/787858.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI赋能,全面筑牢防线:重点非煤矿山重大灾害风险防控系统探析

一、背景需求 随着工业化和现代化的快速发展&#xff0c;非煤矿山作为重要的资源开采基地&#xff0c;其安全生产问题日益受到社会各界的广泛关注。非煤矿山在开采过程中&#xff0c;面临着诸多重大灾害风险&#xff0c;如滑坡、坍塌、水害、火灾等&#xff0c;这些灾害一旦发…

C基础day7

一、思维导图 二、课后练习 1、提示并输入一个字符串&#xff0c;统计该字符串中字母、数字、空格以及其他字符的个数 #include<myhead.h> #define M 20 int main(int argc, const char *argv[]) {int sum_a0,sum_b0,sum_c0,sum_d0;char str[M];printf("please en…

用流式数据库解决「自动化检测服务器性能异常」难题

对 DevOps 团队来说&#xff0c;检测大量服务器的性能异常并尽快响应一直是个挑战。他们设置了各种指标来监控服务器性能&#xff0c;但诊断性能问题复杂且耗时&#xff0c;因为诊断数据的量可能非常大。越来越多的人认为这个过程应该自动化。但怎么做呢&#xff1f; 流式系统…

Chromium编译指南2024 Linux篇-同步Chromium第三方库(四)

1.引言 在成功拉取Chromium源码并创建新分支后&#xff0c;我们需要进一步配置开发环境。这包括拉取必要的第三方库以及设置hooks&#xff0c;以确保我们能够顺利进行编译和开发工作。以下步骤将详细介绍如何进行这些配置。 2.拉取第三方库以及hooks Chromium 使用了大量的第…

2024年7月1日,公布的OpenSSH的漏洞【CVE-2024-6387】

目录 ■概要 ■概要&#xff08;日语&#xff09; ■相关知识 openssh 和 ssh 有区别吗 如何查看 openssh的版本 漏洞描述 glibc Linux是什么 如何查看系统是不是基于 Gibc RHEL Linux 是基于Glibc的Linux吗 还有哪些 Linux版本是基于 GNU C库&#xff08;glibc&…

opencv读取视频文件夹内视频的名字_时长_帧率_分辨率写入excel-cnblog

看视频的时候有的视频文件名贼长。想要翻看&#xff0c;在文件夹里根本显示不出来&#xff0c;缩短又会丢失一些信息&#xff0c;所以我写了一份Python代码&#xff0c;直接获取视频的名字&#xff0c;时长&#xff0c;帧率&#xff0c;还有分辨率写到excel里。 实际效果如下图…

【2024——CUMCM】Matlab快速入门

目录 常识 disp and input 字符串合并 sum 提取矩阵指定位置的元素 指定行列 指定行or指定列&#xff08;返回行/列向量&#xff09; 指定某些行 指定全部元素&#xff0c;按列拼接 size repmat 矩阵的运算 基本运算 形状相同的矩阵运算 每个元素同时和常数相乘或相…

gitee及git的简单使用、下载教(保姆级教程)

前言&#xff1a; GitHub&#xff0c;一个由外国研发的代码开源网站&#xff0c;我们可以通过它获得别人优秀的项目源码&#xff0c;也可以在上面上传自己的劳动成果。但是&#xff0c;我们很难访问外网。于是&#xff0c;我们将目光转向国内一个类似的网站---码云&#xff08…

基于jeecgboot-vue3的Flowable流程-集成仿钉钉流程(四)支持json和xml的显示

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、相应的界面前端代码 <template><div class"formDesign"><FlowDesign :process"process" :fields"fields" :readOnly"readOnly&quo…

OR-3H7-4晶体管光耦,可对标替代TLP281-4等

提供隔离反馈 逻辑电路之间的接口 提供1通道和4通道 电平转换 DC和AC输入 SMPS中的调节反馈电路 消除接地环路 特征 电流传输比&#xff1a;IF 1mA&#xff0c;VCE 5V&#xff0c;Ta 25 C时最小50% 高输入输出隔离电压。&#xff08;VISO3&#xff0c;750Vrms&#xf…

MySQL GROUP_CONCAT 函数详解与实战应用

提示&#xff1a;在需要将多个值组合成一个列表时&#xff0c;GROUP_CONCAT() 函数为 MySQL 提供了一种强大的方式来处理数据 文章目录 前言什么是 GROUP_CONCAT()基本语法 示例使用 GROUP_CONCAT()去除重复值排序结果 前言 提示&#xff1a;这里可以添加本文要记录的大概内容…

16:9横屏短视频素材库有哪些?横屏短视频素材网站分享

在这个视觉内容至关重要的时代&#xff0c;16:9横屏视频因其宽广的画面和优越的观赏体验&#xff0c;已经成为无数创作者和营销专家的首选格式。但要创造出吸引人的横屏视频&#xff0c;高质量的视频素材库是不可或缺的。不管你是资深视频制作人还是刚入行的新手&#xff0c;下…

Proteus + Keil单片机仿真教程(五)多位LED数码管的静态显示

Proteus + Keil单片机仿真教程(五)多位LED数码管 上一章节讲解了单个数码管的静态和动态显示,这一章节将对多个数码管的静态显示进行学习,本章节主要难点: 1.锁存器的理解和使用; 2.多个数码管的接线封装方式; 3.Proteus 快速接头的使用。 第一个多位数码管示例 元件…

Maven在Windows中的配置方法

本文介绍在Windows电脑中&#xff0c;下载、配置Maven工具的详细方法。 Maven是一个广泛使用的项目管理工具&#xff0c;主要针对Java项目&#xff0c;但也可以用于其他类型的项目&#xff1b;其由Apache软件基金会维护&#xff0c;旨在简化和标准化项目构建过程&#xff0c;依…

库存软件有永久免费版吗?

随着商品种类的增加和供应链的日益复杂&#xff0c;如何高效、准确的追踪库存数量&#xff0c;预测需求&#xff0c;减少积压或缺货&#xff0c;成为许多商家头疼的问题&#xff0c;找一款合适的库存管理软件成为当务之急。但是&#xff0c;高昂的购买成本让中小企业望而却步。…

职场必看:如何用AI打造完美简历和面试准备

如何用AI打造完美简历和面试准备 1. 未来简历AI平台:开启个性化简历制作 想要在职场上留下深刻印象?首先,你需要一份出色的简历。未来简历AI平台让你通过简单的扫码和输入信息,快速开始简历制作。 2. 简历模板:选择适合你的岗位模板 面对众多简历模板,如何挑选?平…

鸿蒙语言基础类库:【@ohos.util.ArrayList (线性容器ArrayList)】

线性容器ArrayList 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 …

Windows11配置WSL2支持代理上网

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装WSL2分发版二、配置步骤三、测试总结 前言 说起来本来这个功能我也不需要的&#xff0c;只是最近突然有个需求就顺便研究了下&#xff0c;WSL2默认的网…

Cesium自定义着色器构件三角面片【闪烁】问题,但是一移动视角就闪烁

问题&#xff1a;已知各个顶点的坐标信息、颜色和索引信息&#xff0c;并自定义绘制三角面片。 但是绘制的三角面片随着视角稍微改动就会出现闪烁现象&#xff01;&#xff01;&#xff01;why? Cesium数据类型的精度问题&#xff0c;例如下面为了获取能接收到高精度坐标信息…

【自动驾驶/机器人面试C++八股精选】专栏介绍

目录 一、自动驾驶和机器人技术发展前景二、C在自动驾驶和机器人领域的地位三、专栏介绍四、订阅需知 一、自动驾驶和机器人技术发展前景 随着人工智能、机器学习、传感器技术和计算能力的进步&#xff0c;自动驾驶和机器人的技术水平不断提升&#xff0c;使得它们更加智能、可…