代码随想录-Day39

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?
在这里插入图片描述
输入:m = 3, n = 7
输出:28
在这里插入图片描述

方法一:

  /**
     * 1. 确定dp数组下标含义 dp[i][j] 到每一个坐标可能的路径种类
     * 2. 递推公式 dp[i][j] = dp[i-1][j] dp[i][j-1]
     * 3. 初始化 dp[i][0]=1 dp[0][i]=1 初始化横竖就可
     * 4. 遍历顺序 一行一行遍历
     * 5. 推导结果 。。。。。。。。
     *
     * @param m
     * @param n
     * @return
     */
    public static int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];
        //初始化
        for (int i = 0; i < m; i++) {
            dp[i][0] = 1;
        }
        for (int i = 0; i < n; i++) {
            dp[0][i] = 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];
    }

这段代码是使用Java实现解决“机器人行走的路径数目”问题,即在一个m x n的网格中,从左上角(0,0)到右下角(m-1,n-1)有多少种行走路径,每次只能向右或向下移动。该问题可以用动态规划(Dynamic Programming, DP)来解决。

代码解析

  1. 确定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]。这是因为机器人到达(i, j)只能是从(i-1, j)向右走一步或从(i, j-1)向下走一步。

  3. 初始化:边界条件是网格的第一行和第一列,因为从这些边缘出发到它们自身的路径只有一条,即dp[i][0]=1(第一列)和dp[0][i]=1(第一行)。

  4. 遍历顺序:按照从上到下、从左到右的顺序遍历网格,先处理好边界和第一行第一列后,逐行逐列计算内部点的路径数目。

  5. 推导结果:最终,dp[m-1][n-1]即为到达右下角的路径总数。

示例

假设m=3n=3,即一个3x3的网格,按照上述代码执行,dp数组会逐步填充如下:

初始状态(考虑初始化):

1 1 1
1 1 1
1 1 1

迭代填充后(忽略边界,根据递推公式):

1 2 3
1 3 6
1 4 10

最终,dp[2][2] = 10,表示从左上角到右下角有10种不同的行走路径。

通过这个动态规划方法,我们可以高效地解决这类路径计数问题。

方法二:

class Solution {
    public int uniquePaths(int m, int n) {
        // 在二维dp数组中,当前值的计算只依赖正上方和正左方,因此可以压缩成一维数组。
        int[] dp = new int[n];
        // 初始化,第一行只能从正左方跳过来,所以只有一条路径。
        Arrays.fill(dp, 1);
        for (int i = 1; i < m; i ++) {
            // 第一列也只有一条路,不用迭代,所以从第二列开始
            for (int j = 1; j < n; j ++) {
                dp[j] += dp[j - 1]; // dp[j] = dp[j] (正上方)+ dp[j - 1] (正左方)
            }
        }
        return dp[n - 1];
    }
}

这段Java代码是解决“机器人行走的路径数目”问题的另一种实现,采用了一维动态规划数组来减少空间复杂度。给定一个m x n的网格,从左上角(0,0)到右下角(m-1,n-1)有多少种行走路径,每次只能向右或向下移动。下面是代码的详细解析:

代码解析

  1. 初始化一维dp数组:由于计算某个位置的路径数只与其正上方和正左方的路径数有关,因此可以使用一维数组dp来存储到达每列底部的路径总数。数组长度为n,初始化时考虑到第一行的每个位置都只有1条路径(即自己到自己),所以使用Arrays.fill(dp, 1);初始化。

  2. 双层循环遍历:外层循环遍历网格的行(从1开始,因为第一行已经初始化),内层循环遍历网格的列(从1开始,因为第一列在初始化时已设定为1)。

  3. 状态转移方程:对于dp[j],即到达第i行第j列的路径数,可以通过累加左边一格的路径数dp[j - 1]得到,即dp[j] += dp[j - 1];。这样做是因为到达(i, j)的路径可以看作是从(i - 1, j)向下走一条路径加上从(i, j - 1)向右走一条路径的所有组合。

  4. 返回结果:遍历完成后,dp[n - 1]存储了到达右下角的路径总数,直接返回这个值即可。

优势

  • 空间优化:相比于二维数组的解法,这里只使用了一维数组来存储每行的结果,从而将空间复杂度从O(mn)降低到了O(n),对于大规模的m和n来说,这是一个显著的优势。
  • 计算逻辑简洁:通过巧妙地复用一维数组,避免了额外的空间开销,同时也保持了逻辑的直观性。

示例

假设m=3n=3,执行上述代码后,dp数组会经历以下变化:

  • 初始化后:dp = [1, 1, 1]
  • 第一轮外层循环后:dp = [1, 2, 3] (到达第二行各列的路径数)
  • 第二轮外层循环后:dp = [1, 3, 6] (到达第三行各列的路径数,最终结果)

最终返回dp[n - 1] = 6,表示从左上角到右下角有6种不同的行走路径。

63. 不同路径 II

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 1 和 0 来表示。
在这里插入图片描述
输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:

  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右
    在这里插入图片描述
    输入:obstacleGrid = [[0,1],[0,0]]
    输出:1

方法一:

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[][] dp = new int[m][n];

        //如果在起点或终点出现了障碍,直接返回0
        if (obstacleGrid[m - 1][n - 1] == 1 || obstacleGrid[0][0] == 1) {
            return 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++) {
                dp[i][j] = (obstacleGrid[i][j] == 0) ? dp[i - 1][j] + dp[i][j - 1] : 0;
            }
        }
        return dp[m - 1][n - 1];
    }
}

这段Java代码是用于解决“有障碍的格子”的独特路径问题。给定一个二维数组obstacleGrid,其中0表示可以通过的格子,1表示障碍物。你要从左上角(0,0)移动到右下角(m-1, n-1),每次只能向右或向下移动。该函数计算并返回从起点到终点的不同路径数目,如果无法到达终点则返回0。

代码解析:

  1. 初始化变量:首先获取网格的行数m和列数n,并创建一个与obstacleGrid同样大小的二维数组dp来存储到达每个格子的路径数。

  2. 检查起点和终点:如果终点或起点有障碍物,直接返回0,因为这意味着没有路径可达。

  3. 初始化边界条件:遍历第一列和第一行,如果格子不是障碍物,将其路径数设为1。这表示从起点开始向右或向下有一条路径。

    • 对于第一列:dp[i][0] = 1(若无障碍)
    • 对于第一行:dp[0][j] = 1(若无障碍)
  4. 动态规划填充dp数组:从第二行和第二列开始,对于每个格子(i, j),如果该格子不是障碍物,则其路径数为上面格子(i-1, j)的路径数加上左边格子(i, j-1)的路径数,即dp[i][j] = dp[i - 1][j] + dp[i][j - 1]。如果遇到障碍物,则该位置的路径数设为0,因为不能通过。

  5. 返回结果:最后,dp[m - 1][n - 1]存储了到达终点的路径数目,返回该值作为结果。

示例

假设obstacleGrid为:

[
  [0, 0, 0],
  [0, 1, 0],
  [0, 0, 0]
]

运行这段代码会返回2,因为从左上角到右下角只有两条路径可走,且中间的障碍物阻止了其他可能的路径。

方法二:

// 空间优化版本
class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        int m = obstacleGrid.length;
        int n = obstacleGrid[0].length;
        int[] dp = new int[n];

        for (int j = 0; j < n && obstacleGrid[0][j] == 0; j++) {
            dp[j] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (obstacleGrid[i][j] == 1) {
                    dp[j] = 0;
                } else if (j != 0) {
                    dp[j] += dp[j - 1];
                }
            }
        }
        return dp[n - 1];
    }
}

这段代码是针对“有障碍的格子”的独特路径问题的空间优化版本。与之前的解决方案相比,这里使用了一维数组dp来代替二维数组,从而节省了空间。代码依然计算从二维数组obstacleGrid的左上角到右下角的无障碍路径数量,其中1表示障碍物,0表示可通过的路径。

代码解析

  1. 初始化变量:获取网格的行数m和列数n,并声明一个一维数组dp,长度为n,用于记录到达当前列的路径数。

  2. 初始化第一行:遍历第一行的列,只要遇到无障碍物(obstacleGrid[0][j] == 0),就将dp[j]设为1,表示从起点到这一列有一条路径。

  3. 动态规划填充dp数组

    • 遍历每一行(从第二行开始),对于每一行中的每一列:
      • 如果当前格子是障碍物,那么到达该格子的路径数为0,因此dp[j] = 0
      • 如果当前格子不是障碍物,有两种情况:
        • 如果是第一列(j == 0),则只能从上方到达,因此dp[j] = dp[j](保持不变,实际上这种情况在循环外已处理,此处默认处理下一列的情况)。
        • 如果不是第一列,到达当前格子的路径数等于上方格子的路径数加上左侧格子的路径数(因为只能向上或向左移动),即dp[j] += dp[j - 1]
  4. 返回结果:最后返回dp[n - 1],即到达终点的路径数量。

优点

  • 空间优化:相比使用二维数组的解法,这里仅使用一维数组存储每行的路径数,将空间复杂度从O(mn)降低到O(n),在处理大尺寸矩阵时更加高效。

示例

假设obstacleGrid为:

[
  [0, 0, 0],
  [0, 1, 0],
  [0, 0, 0]
]

该代码会返回2,表示有两条路径可以从左上角走到右下角,且避开了中间的障碍物。

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

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

相关文章

服务器硬件及RAID配置

目录 一、RAID磁盘阵列 1.概念 2.RAID 0 3.RAID 1 4.RAID 5 5.RAID 6 6.RAID 10 二、阵列卡 1.简介 2.缓存 三、创建 1.创建RAID 0 2.创建RAID 1 3.创建RAID 5 4.创建RAID 10 四、模拟故障 一、RAID磁盘阵列 1.概念 &#xff08;1&#xff09;是Redundant Array …

【Java毕业设计】基于JavaWeb的房屋出租系统

本科毕业设计论文 题目&#xff1a;房屋交易平台设计与实现 系 别&#xff1a; XX系&#xff08;全称&#xff09; 专 业&#xff1a; 软件工程 班 级&#xff1a; 软件工程15201 学生姓名&#xff1a; 学生学号&#xff1a; 指导教师&#xff1a; 导师1 导师2 文章目录 摘…

【Linux】性能分析器 perf 详解(一)

1、简介 perf 是由 Linux 官方提供的系统性能分析工具 。它包含两部分: perf_events ,Linux 内核中的一个子系统perf 命令,用户空间的应用程序内核子系统 perf_events 提供了性能计数器(hardware performance counters)和性能事件的支持,它以事件驱动型的方式工作,通过…

数据独立性-信息、数据、数据结构、数据独立性

一、引言 同人工管理数据和文件系统管理数据相比&#xff0c;数据库管理数据最主要的优点是数据独立性高。数据独立性是数据库领域中的一个常用术语和重要概念&#xff0c;包括数据的物理独立性和逻辑独立性 二、数据与数据结构 1、信息 在数据管理领域&#xff0c;数据与信…

PWN练习---Heap_1

heap_Easy_Uaf 题源&#xff1a;PolarD&N 考点&#xff1a;UAF漏洞(use after free) 源码 程序是一个菜单&#xff0c;可以实现add&#xff0c;dele&#xff0c;edit&#xff0c;puts 堆块内容等的功能。&#xff08;堆块编号从0开始&#xff09; 注意到一个存在backdoor的…

LKD-Net: Large Kernel Convolution Network for Single Image Dehazing

LKD-Net&#xff1a;用于单幅图像去噪的大型核卷积网络 摘要 基于深度卷积神经网络(CNN)的单幅图像去噪方法已经取得了很大的成功。以往的方法致力于通过增加网络的深度和宽度来提高网络的性能。目前的方法侧重于增加卷积核的大小&#xff0c;以受益于更大的接受野来增强其性能…

MySQL——联表查询JoinON详解

Join 对比&#xff08;7种&#xff09; 代码演示&#xff1a; -- 查询参加了考试的同学&#xff08;学号&#xff0c;姓名&#xff0c;科目编号&#xff0c;分数&#xff09; SELECT * FROM student SELECT * FROM result/* 1. 分析需求&#xff1a;分析查询的字段来自哪些表&…

【Android】android studio简单实现图书馆借阅管理系统

希望文章能给到你启发和灵感&#xff5e; 点赞收藏关注 支持一下吧&#xff5e; 阅读指南 序幕一、基础环境说明1.1 硬件环境1.2 软件环境 二、整体设计2.1 数据库逻辑处理&#xff1a;2.2 登录/注册模块2.3 功能界面初始化&#xff1a;2.4 图书管理模块2.5 图书租借服务2.6 读…

UFS协议—新手快速入门(四)【10】

目录 十、UPIU数据包格式详解 1、Transaction Type&#xff08;类型&#xff09; 2、Flags&#xff08;附加信息&#xff09; 其它 3、LUN&#xff08;逻辑单元号&#xff09;&#xff1a; 4、Task Tag&#xff08;任务标签&#xff09;&#xff1a; 5、Command Type&…

Ubuntu22 更新内核后终端输入卡顿,最简单的解决方案

在系统升级后相信很多人都遇到了这个问题&#xff0c;系统终端输入卡顿&#xff0c;但是ssh远程进来不卡&#xff0c;使用第三方终端也不卡,…&#xff0c;今天终于忍不了&#xff0c;解决了 现象&#xff1a; 更新Nvidia驱动后,内核进行了自动编译升级。 之后的一段时间使用…

银幕光影交织,红酒香醇流淌,一场电影与红酒的绝美浪漫邂逅

在光影交错的世界里&#xff0c;红酒与电影总能在不经意间碰撞出浪漫的火花。当银幕上的角色轻启瓶盖&#xff0c;那迷人的酒香便如诗如画般弥漫开来&#xff0c;与影片的情节交织在一起&#xff0c;构成了一幅幅动人的画面。今天&#xff0c;就让我们一起走进这个充满酒香的银…

以太网的基本介绍

文章目录 一、以太网&#xff08;Ethernet&#xff09;介绍二、协议介绍三、什么是PHY&#xff1f;三、PHY芯片介绍1.标准接口协议&#xff1a;2.寄存器配置&#xff1a;3.自动协商&#xff1a;4.链路检测&#xff1a;5.复位与电源管理&#xff1a;6.中断与状态报告&#xff1a…

Linux(简单概述)

目录 第一章 初识Linux 第四章 文件管理与常用命令 1.文件基础知识 2.文件显示命令 3.文件内容查询 4. 文件和目录基本操作 5. 文件复制、移动、删除 7. 链接 8. 文件访问权限 9. 文件查找命令 10. 压缩和解压缩 第五章用户与用户组 第六章软件包管理RPM和YUM数据库…

数据结构-线性表的链式表示

目录 前言一、线性表的链式表示和实现1.1 线性表的表示1.2 基本操作的实现1.3 线性表的链式表示的优缺点 总结 前言 本篇文章主要介绍线性表的链式表示 一、线性表的链式表示和实现 1.1 线性表的表示 线性表的链式表示又称为链式存储结构或链式映像 链式存储定义&#xff1…

hypernetwork在SD中是怎么工作的

大家在stable diffusion webUI中可能看到过hypernetwork这个词&#xff0c;那么hypernetwork到底是做什么用的呢&#xff1f; 简单点说&#xff0c;hypernetwork模型是用于修改样式的小型神经网络。 什么是 Stable Diffusion 中的hypernetwork&#xff1f; Hypernetwork 是由…

Linux:RAID磁盘阵列

目录 一、RAID&#xff08;磁盘阵列&#xff09; 1.1、概念 1.2、RAID 0&#xff08;条带化存储&#xff09; 1.3、RAID 1&#xff08;镜像存储&#xff09; 1.4、RAID 5 1.5、RAID 6 1.6、RAID 10 (先做镜像&#xff0c;再做条带) 二、创建RAID 2.1、建立RAID 0 …

视频录制软件哪个好用?5款简单好用软件推荐

在我们的日常生活中&#xff0c;都有哪些好用的视频录制软件&#xff1f;在很多场合中我们都会用电脑记录下重要的时刻。比如&#xff0c;在电脑上听老师讲解一道难题的方法时&#xff0c;怕自己会忘记&#xff0c;想要录制下来进行重复的观看。这时&#xff0c;选择一款好用的…

Qt添加Dialog对话框

Qt版本&#xff1a;5.12.12 1.添加【模块】 Base class&#xff1a;可以选择QDialog、QWidget、QMainWindow 会自动生成MyDialog.h和MyDialog.cpp文件以及MyDialog.ui文件&#xff0c; 2.添加代码&#xff1a; &#xff08;1&#xff09;TestDialog.h #pragma once#include…

【Matlab 六自由度机器人】机器人动力学之推导拉格朗日方程(附MATLAB机器人动力学拉格朗日方程推导代码)

【Matlab 六自由度机器人】机器人动力学概述 近期更新前言正文一、拉格朗日方程的推导1. 单自由度系统2. 单连杆机械臂系统3. 双连杆机械臂系统 二、MATLAB实例推导1. 机器人模型的建立2. 动力学代码 总结参考文献 近期更新 【汇总】 【Matlab 六自由度机器人】系列文章汇总 …

【代码】python实现一个BP神经网络-原理讲解与代码展示

​ 本文来自《老饼讲解-BP神经网络》https://www.bbbdata.com/ 目录 一、BP神经网络原理回顾1.1 BP神经网络的结构简单回顾1.2.BP神经网络的训练算法流程 二、python实现BP神经网络代码2.1.数据介绍2.2.pytorch实现BP神经网络代码 在python中要如何使用代码实现一个BP神经网络呢…