代码随想录训练营 Day34打卡 动态规划 part02 62. 不同路径 63. 不同路径 II 343. 整数拆分 96. 不同的二叉搜索树

news2024/11/18 8:38:29

代码随想录训练营 Day34打卡 动态规划 part02

一、力扣62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例:
在这里插入图片描述
输入:m = 3, n = 7
输出:28

想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]。

此时在回顾一下 dp[i - 1][j] 表示啥,是从(0, 0)的位置到(i - 1, j)有几条路径,dp[i][j - 1]同理。

那么很自然,dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个方向过来。

如何初始化呢,首先dp[i][0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0][j]也同理。

所以初始化代码为:

for (int i = 0; i < m; i++) dp[i][0] = 1;
for (int j = 0; j < n; j++) dp[0][j] = 1;

举例推导:
在这里插入图片描述

代码实现

class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        # 创建一个 m x n 的二维列表 dp,其中 dp[i][j] 表示到达单元格 (i, j) 的唯一路径数
        dp = [[0] * n for _ in range(m)]
        
        # 初始化第一列:从起点 (0, 0) 到任意单元格 (i, 0) 的路径数都为 1,
        # 因为只能一直往下走
        for i in range(m):
            dp[i][0] = 1
        
        # 初始化第一行:从起点 (0, 0) 到任意单元格 (0, j) 的路径数都为 1,
        # 因为只能一直往右走
        for j in range(n):
            dp[0][j] = 1
        
        # 遍历剩余的单元格,计算每个单元格的唯一路径数
        # dp[i][j] 的值等于从上方单元格 (i-1, j) 和左侧单元格 (i, j-1) 到达的路径数之和
        for i in range(1, m):
            for j in range(1, n):
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        
        # 返回右下角单元格 (m-1, n-1) 的唯一路径数
        return dp[m - 1][n - 1]

力扣题目链接
题目文章讲解
题目视频讲解

二、 力扣63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
网格中的障碍物和空位置分别用 1 和 0 来表示。
示例:
在这里插入图片描述
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1.向右 -> 向右 -> 向下 -> 向下
2.向下 -> 向下 -> 向右 -> 向右

递推公式和62.不同路径一样,dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。

但这里需要注意一点,因为有了障碍,(i, j)如果就是障碍的话应该就保持初始状态(初始状态为0)。

因为从(0, 0)的位置到(i, 0)的路径只有一条,所以dp[i][0]一定为1,dp[0][j]也同理。

但如果(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后的dp[i][0]应该还是初始值0。
如图:

在这里插入图片描述
下标(0, j)的初始化情况同理。
举例如题:
在这里插入图片描述
对应的dp table 如图:

在这里插入图片描述

代码实现

class Solution:
    def uniquePathsWithObstacles(self, obstacleGrid):
        # 获取网格的行数和列数
        m = len(obstacleGrid)
        n = len(obstacleGrid[0])
        
        # 如果起点 (0, 0) 或终点 (m-1, n-1) 处有障碍物,直接返回 0,因为无法到达终点
        if obstacleGrid[m - 1][n - 1] == 1 or obstacleGrid[0][0] == 1:
            return 0
        
        # 创建一个 m x n 的二维列表 dp,用于存储到达每个单元格的唯一路径数
        dp = [[0] * n for _ in range(m)]
        
        # 初始化第一列
        # 如果某个单元格 (i, 0) 没有障碍物,则 dp[i][0] = 1(表示从起点到这个单元格有一条路径)
        # 如果某个单元格 (i, 0) 有障碍物,则后面的所有单元格都无法到达,因此直接退出循环
        for i in range(m):
            if obstacleGrid[i][0] == 0:  # 当前单元格没有障碍物
                dp[i][0] = 1
            else:  # 当前单元格有障碍物,后续单元格不可达
                break
        
        # 初始化第一行
        # 类似地,如果某个单元格 (0, j) 没有障碍物,则 dp[0][j] = 1
        # 如果某个单元格 (0, j) 有障碍物,则后面的所有单元格都无法到达,因此直接退出循环
        for j in range(n):
            if obstacleGrid[0][j] == 0:  # 当前单元格没有障碍物
                dp[0][j] = 1
            else:  # 当前单元格有障碍物,后续单元格不可达
                break
        
        # 计算剩余网格中的每个单元格的唯一路径数
        for i in range(1, m):
            for j in range(1, n):
                # 如果当前单元格 (i, j) 有障碍物,路径数保持为 0,跳过该单元格
                if obstacleGrid[i][j] == 1:
                    continue
                # 如果没有障碍物,当前单元格的路径数等于从上方单元格和左侧单元格到达的路径数之和
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
        
        # 返回右下角单元格 (m-1, n-1) 的路径数,即为最终答案
        return dp[m - 1][n - 1]

力扣题目链接
题目文章讲解
题目视频讲解

三、 力扣343. 整数拆分

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
返回 你可以获得的最大乘积 。
示例:
输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。

dp[i]最大乘积是怎么得到的呢?
一个是j * (i - j) 直接相乘。
一个是j * dp[i - j],相当于是拆分(i - j),对这个拆分不理解的话,可以回想dp数组的定义。

举例当n为10 的时候,dp数组里的数值,如下:
在这里插入图片描述

代码实现

class Solution:
    # 假设对正整数 i 拆分出的第一个正整数是 j(1 <= j < i),则有以下两种方案:
    # 1) 将 i 拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j * (i-j)
    # 2) 将 i 拆分成 j 和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j * dp[i-j]
    def integerBreak(self, n):
        # 创建一个大小为 n+1 的数组 dp,用于存储从 1 到 n 的每个数字的最大乘积
        dp = [0] * (n + 1)   

        # 初始化 dp[2] 为 1,因为当 n=2 时,只有一种拆分方式:1+1=2,乘积为 1
        dp[2] = 1  
       
        # 从 3 开始计算,直到 n
        for i in range(3, n + 1):
            # 遍历所有可能的第一个拆分点 j (1 <= j < i),j 最大不超过 i 的一半
            for j in range(1, i // 2 + 1):
                # 对于每个拆分点 j,有以下三种情况需要比较:
                # 1) 直接将 i 拆分成 j 和 i-j 的和,乘积为 j * (i-j)
                # 2) 将 i 拆分成 j 和 i-j 的和,并将 i-j 继续拆分成多个正整数,乘积为 j * dp[i-j]
                # 3) 之前计算得到的 dp[i](即尚未考虑当前拆分点 j 的最大乘积)
                dp[i] = max(dp[i], (i - j) * j, dp[i - j] * j)
        
        # 返回 dp[n],即将 n 拆分为多个正整数的最大乘积
        return dp[n]

力扣题目链接
题目文章讲解
题目视频讲解

四、 力扣96. 不同的二叉搜索树

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例:
在这里插入图片描述
输入:n = 3
输出:5

我们应该先举几个例子,画画图,看看有没有什么规律,如图:
在这里插入图片描述
在这里插入图片描述

元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量

有2个元素的搜索树数量就是dp[2]。

有1个元素的搜索树数量就是dp[1]。

有0个元素的搜索树数量就是dp[0]。

所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]

如图所示:
在这里插入图片描述
dp[i] : 1到i为节点组成的二叉搜索树的个数为dp[i]。

在上面的分析中,其实已经看出其递推关系, dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]

j相当于是头结点的元素,从1遍历到i为止。

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

n为5时候的dp数组状态如图:

在这里插入图片描述

代码实现

class Solution:
    def numTrees(self, n: int) -> int:
        # 创建一个长度为 n+1 的数组 dp,用于存储从 1 到 n 的每个数字组成的二叉搜索树的数量
        dp = [0] * (n + 1)  

        # 当 n 为 0 时,只有一种情况,即空树,所以 dp[0] = 1
        dp[0] = 1  
        
        # 外层循环:从 1 到 n,计算每个 i 对应的二叉搜索树的数量
        for i in range(1, n + 1):  
            # 内层循环:对于每个 i,选择每个 j 作为根节点,计算不同二叉搜索树的数量
            for j in range(1, i + 1):  
                # 递推公式:dp[i] += dp[j - 1] * dp[i - j]
                # dp[j - 1] 表示以 j 为根节点的左子树的节点数量的组合情况
                # dp[i - j] 表示以 j 为根节点的右子树的节点数量的组合情况
                dp[i] += dp[j - 1] * dp[i - j]  
        
        # 返回以 1 到 n 为节点的二叉搜索树的总数量
        return dp[n]

力扣题目链接
题目文章讲解
题目视频讲解

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

5.vue中axios封装工程化

vue工程化中axios封装 视频演示地址&#xff1a;https://www.bilibili.com/video/BV121egeQEHg/?vd_source0f4eae2845bd3b24b877e4586ffda69a 通常我们封装需要封装request.js基础的发送请求工具类&#xff0c;再根据业务封装service类&#xff0c;service类是具体业务的接口…

Ps:首选项 - 暂存盘

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“暂存盘” Scratch Disks选项卡通过合理配置和管理暂存盘&#xff0c;可以显著提高 Photoshop 的运行性能&#xff0c;特别是在处理复杂的设计项目或大型图像文件时。选择合适…

Conformer解析

文章目录 前言Conformer encoder model architectureConvolution SubsamplingLinear & DropoutConformer BlocksFeed Forward ModuleMulti-Headed Self-Attention ModuleConvolution Module 前言 Conformer encoder model architecture 对于语音来说&#xff0c;整个架构…

内核链表的实现

内核链表和普通链表的区别&#xff1a; 1. 普通链表当中数据域和指针域&#xff0c;没有做到区分&#xff0c;数据与指针形成了一个整体&#xff0c;而内核链表数据与指针是完全剥离的没有直接的关系。 2. 在普通链表当中所有节点的数据都是一样的类型&#xff0c;而内核链表…

vmware安装openEuler操作系统

文章目录 vmware安装openEuler操作系统官网地址下载方式 新建虚拟机新建虚拟机新建虚拟机向导输入新建的虚拟机名称预览安装虚拟机的操作系统开始安装语言选项基础配置 网络配置关机拍快照克隆模板机 vmware安装openEuler操作系统 官网地址 https://www.openeuler.org/zh/dow…

6.4 MySQL 常见查询优化案例分析

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

Python 函数参数介绍

目录 函数 -- 普通参数 函数 -- 参数进一步 -- 指定参数 函数 -- 参数带 * -- 将传入的参数打包成元组 函数 -- 参数带 ** -- 关键字传参 -- 将传入的参数打包成字典 综合使用 函数 -- 普通参数 函数 -- 参数进一步 -- 指定参数 函数调用时&#xff0c;未指定的参数需要写…

风清扬/基于Java语言的光伏监控系统+光伏发电预测+光伏项目+光伏运维+光伏储能项目

基于Java语言的光伏监控系统光伏发电预测光伏项目光伏运维光伏储能项目 介绍 基于Java语言的光伏监控系统光伏发电系统光伏软件系统光伏监控系统源码光伏发电系统源码 基于Java语言的光伏监控系统光伏发电预测光伏项目光伏运维光伏储能项目 安装教程 参与贡献 Fork 本仓库新…

十分钟搭建一个RTMP服务器

使用SRS搭建RTMP服务器 如果您需要搭建一个RTMP服务器&#xff0c;您可以使用SRS&#xff08;Simple-RTMP-Server&#xff09;来完成此任务。SRS是一个开源的RTMP服务器下面是一个简单的步骤指南&#xff1a; 获取srs srs官⽹&#xff1a;https://github.com/ossrs/srs 码云…

软考2024下半年软考报名时间安排及报名流程

简介 软件资格考试也称计算机软件水平考试 [1]&#xff0c;是原中国计算机软件专业技术资格和水平考试的完善与发展。是对从事或准备从事计算机应用技术、网络、信息系统和信息服务等专业技术工作的人员水平和能力的测试。这项考试是由国家人力资源和社会保障部、工业和信息化…

【通信协议】I2C总线(一主多从)

目录 I2C简介 硬件电路 软件模拟初始化 基本单元 起始信号 停止信号 发送一个字节 接收一个字节 发送应答 接收应答 I2C基本单元代码 MyI2C.h MyI2C.c 完整数据帧 学习资料分享 本博客使用软件模拟的代码进行I2C总线​​​​​​&#xff08;总线指多个设备共用…

【Python机器学习系列】使用Hyperopt搜索随机森林分类模型最优超参数(案例+源码)

这是我的第342篇原创文章。 一、引言 Hyperopt是一个强大的python库&#xff0c;用于超参数优化&#xff0c;由jamesbergstra开发。Hyperopt使用贝叶斯优化的形式进行参数调整&#xff0c;允许你为给定模型获得最佳参数。它可以在大范围内优化具有数百个参数的模型。 在本节中…

Vue3.0生命周期钩子(包含:Vue 2.0 和 Vue 3.0)

1、Vue 2.0 生命周期钩子 每个应用程序实例在创建时都有一系列的初始化步骤。例如&#xff0c;创建数据绑定、编译模板、将实例挂载到 DOM 并在数据变化时触发 DOM 更新、销毁实例等。在这个过程中会运行一些叫做生命周期钩子的函数&#xff0c;通过这些钩子函数可以定义业务逻…

【STM32+HAL】巡逻打靶小车

一、前言 作为电赛最爱出的小车和视觉题&#xff0c;将两者结合起来出题也是一个方向&#xff0c;故写下此文供学者参考&#xff0c;也作为备赛电赛的记录。 如有小伙伴想交流学习心得&#xff0c;欢迎加入群聊751950234&#xff0c;群内不定期更新代码&#xff0c;以及提供本…

elasticsearch的高亮查询三种模式查询及可能存在的问题

目录 高亮查询使用介绍 高亮参数 三种分析器 可能存在的查询问题 fvh查询时出现StringIndexOutOfBoundsException越界 检索高亮不正确 参考文档 高亮查询使用介绍 Elasticsearch 的高亮&#xff08;highlight&#xff09;可以从搜索结果中的一个或多个字段中获取突出显…

python:霍夫变换检测直线

霍夫变换检测直线 在Python中&#xff0c;可以使用OpenCV库来实现霍夫变换进行直线检测。 一、原理 1、霍夫变换(Hough Transform) 霍夫变换是图像处理中从图像中识别几何形状的基本方法之一&#xff0c;应用很广泛&#xff0c;也有很多改进算法。主要用来从图像中分离出具有…

Linux中的锁

user2正在进行抢票: 4 user3正在进行抢票: 3 user1正在进行抢票: 2 user4正在进行抢票: 1 user2正在进行抢票: 0 user3正在进行抢票: -1 user1正在进行抢票: -2 int tickets10000; void* getTicket(void* args) {string usernamestatic_cast<const char*>(args);while(…

【C++篇】迈入新世界的大门——初识C++(上篇)

文章目录 C发展历史C起源C版本更新C23小故事 C在工作领域的应用C参考网站及文档书籍编程语言排行榜C难度参考文档书籍参考文档参考书籍 C第一个程序命名空间为什么要使用namespacenamespace定义及规则命名空间使用 C输入&输出名字含义 缺省参数函数重载 C发展历史 C起源 …

新手小白零基础,该怎样学习编程呢?零基础入门到精通,收藏这一篇就够了

零基础编程入门先学什么&#xff1f;编程语言有几百种&#xff0c;我们应该怎么选择。想学习编程&#xff0c;加入互联网行业&#xff0c;哪一个更有前途&#xff1f;在小白学习编程会有各种各样的问题&#xff0c;今天小编我就来为你解答。 一、怎么选择编程语言 编程语言有很…

geomagic怎么删除平面?geomagic怎么修模

在现代三维建模和3D打印技术的发展中&#xff0c;Geomagic作为一款专业的软件工具&#xff0c;广泛应用于逆向工程、产品设计和质量检测等领域。本文将详细介绍geomagic怎么删除平面&#xff1f;geomagic怎么修模&#xff0c;并探讨Geomagic的主要应用领域。通过这些内容&#…