LeetCode63. 不同路径 II

news2024/11/15 14:08:50

63. 不同路径 II

文章目录

    • [63. 不同路径 II](https://leetcode.cn/problems/unique-paths-ii/)
      • 一、题目
      • 二、题解
        • 方法一:二维数组动态规划
        • 方法二:一维数组动态规划


一、题目

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

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

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

网格中的障碍物和空位置分别用 10 来表示。

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-np1jHnOF-1690689693410)(D:\A_WHJ\Computer Science\typora图片\robot1.jpg)]

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cy53IlVH-1690689693411)(D:\A_WHJ\Computer Science\typora图片\robot2.jpg)]

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j]01

二、题解

方法一:二维数组动态规划

步骤一:确认dp数组以及下标的含义

  • dp[i][j] 表示从起点 (0, 0) 到达网格中第 i 行第 j 列的格子时的不同路径数量。

步骤二:确认递推公式

  • 对于非障碍物格子,可以从上方格子和左侧格子到达,因此 dp[i][j] = dp[i-1][j] + dp[i][j-1]。
  • 对于障碍物格子,无法到达,路径数量为0,即 dp[i][j] = 0。

步骤三:数组初始化

  • 初始化一个二维数组 dp,大小为 m 行 n 列,所有元素初始化为0。
  • 如果起点是障碍物,直接返回0,因为无法到达终点。

步骤四:确定遍历顺序

  • 首先处理起点的特殊情况,然后分别初始化第一列和第一行,再进行动态规划遍历填充剩余的格子。

步骤五:举例推导dp数组
让我们通过一个示例来推导 dp 数组。假设输入网格 obstacleGrid 为:

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

进行推导:

  1. 初始化 dp 数组:
dp = [
  [1, 0, 0],
  [0, 0, 0],
  [0, 0, 0]
]
  1. 初始化第一列和第一行:
dp = [
  [1, 1, 1],
  [1, 0, 0],
  [1, 0, 0]
]
  1. 动态规划遍历填充剩余格子:
dp = [
  [1, 1, 1],
  [1, 0, 1],
  [1, 1, 2]
]

最终返回 dp [m-1] [ n-1 ] 即 dp [2] [2] = 2,表示从起点 (0, 0) 到达终点 (2, 2) 的不同路径数量为 2。

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();
        vector<vector<int>> dp(m, vector<int>(n, 0));
        
        // 处理起始点为障碍物的情况
        if (obstacleGrid[0][0] == 0)
            dp[0][0] = 1;
        
        // 初始化第一列,只要遇到障碍物,后面的都不可达
        for (int i = 1; i < m; ++i) {
            if (obstacleGrid[i][0] == 0){
                dp[i][0] = dp[i-1][0];
            }else{
                break;
            }
                
        }
        
        // 初始化第一行,只要遇到障碍物,后面的都不可达
        for (int j = 1; j < n; ++j) {
            if (obstacleGrid[0][j] == 0){
                dp[0][j] = dp[0][j-1];
            }else{
                break;
            }  
        }
        
        // 动态规划遍历
        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];
    }
};

方法二:一维数组动态规划

先给出代码

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();

        vector<int> dp(n, 0);

        // 初始化边界状态
        dp[0] = (obstacleGrid[0][0] == 0) ? 1 : 0;

        // 计算dp数组
        for (int i = 0; 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[i][j]表示从起点到达网格(i, j)位置的不同路径数。假设网格总共有m行、n列,那么起点在dp[0] [0],终点在dp[m-1] [n-1]。那么状态转移方程可以表示为:

dp[i] [j] = dp[i-1] [j] + dp[i] [j-1]

意思是到达当前格子的路径数等于从上方格子和左侧格子的路径数之和。

现在,我们来思考如何用一个一维数组来表示dp数组。注意到,在更新dp[i] [j]时,我们只需要用到上一行dp[i-1] [j]和当前行左侧位置dp[i] [j-1]的值。所以,如果我们用一个一维数组dp[j]来表示当前行的状态,那么在计算dp[i] [j]时,dp[j](下面状态转移方程等号右侧的dp[j],不是左侧)实际上就是上一行的dp[i-1] [j],而dp[j-1]实际上就是当前行左侧位置dp[i] [j-1]

因此,我们可以将状态转移方程改写为:

dp[j] = dp[j] + dp[j-1]

这里的dp[j]表示当前行到达网格(i, j)位置的不同路径数。

由于我们只需要用到上一行的dp数组和当前行左侧位置的值,所以我们可以用一个一维数组dp来更新当前行的状态,然后逐行向下更新,最终得到最后一行dp数组的值,即为从左上角到右下角的不同路径数。

这是不是一种非常好的改进方法?

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

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

相关文章

【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全

引入互斥初识锁互斥量mutex锁原理解析 可重入VS线程安全STL中的容器是否是线程安全的? 死锁 引入 我们写一个多线程同时访问一个全局变量的情况(抢票系统)&#xff0c;看看会出什么bug&#xff1a; // 共享资源&#xff0c; 火车票 int tickets 10000; //新线程执行方法 vo…

rtthread的idle线程不应该长时间堵塞

RT-Thread是一个实时嵌入式操作系统&#xff0c;它的空闲线程&#xff08;Idle Thread&#xff09;是在系统中没有其他任务需要执行时运行的线程。空闲线程通常用于执行一些低优先级的任务或者进行系统的休眠等操作。 RT-Thread的空闲线程不能在线程中堵塞的原因是为了确保系统…

将多张图片拼接成图像矩阵图

用Python实现将12张图片排列成n*m的图像矩阵图 目录 引言环境准备代码实现效果演示总结 引言 在图像处理和图像展示的应用中&#xff0c;将多张图片排列成一个图像矩阵图是一个常见的需求。本博客介绍如何使用Python实现将12张图片排列成n*m的图像矩阵图。 环境准备 为了实…

【动态规划刷题 2】不同路径不同路径 II

不同路径 62 . 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的…

iPhone 安装 iOS 17公测版(Public Beta)

文章目录 步骤1. 备份iPhone资料步骤2. 申请iOS 17 公测Beta 资格步骤3. 下载iOS 16 Beta 公测描述档步骤4. 选择iOS 17 Beta 公测描述档更新项目步骤5. 升级iOS 17 Public Beta 公开测试版 苹果已经开始向大众释出首个iOS 17 公开测试版/ 公测版( iOS 17 Public Beta)&#xf…

【C++】类和对象 - 上

目录 1. 面向过程和面向对象初步认识2. 类的引入3. 类的定义4. 类的访问限定符及封装4.1 访问限定符4.2 封装 5. 类的作用域6. 类的实例化7. 类对象模型7.1 如何计算类的大小7.2 类对象的存储方式猜测7.3 结构体内存对齐规则 8. this指针8.1 引出8.2 this指针的特性 总结 1. 面…

ES查看集群信息(健康状态、分片、索引等)

1、查看集群状态使用频率最高的方法 http://192.168.1.101:9200/ 注意&#xff1a;不同环境的ip不同 一般我们通过这个方式来验证ES服务器是否启动成功。 2、_cat/health 查看集群健康状态 [rootCENTOS01 ~]# curl http://192.168.1.101:9200/_cat/health?v epoch tim…

Spring Data【Spring Data Redis、Spring Data ElasticSearch】(二)-全面详解(学习总结---从入门到深化)

目录 四、 Spring Data Redis 五、 Spring Data ElasticSearch 四、 Spring Data Redis Redis 是一个基于内存的数据结构存储系统&#xff0c;它可以用作数据库或者缓存。它支持多种 类型的数据结构&#xff0c;这些数据结构类型分别为 String&#xff08;字符串&#xff09…

0基础系列C++教程 从0开始 第四课

目录 来学习新的内容吧 1 输入 第四课课后习题1&#xff1a; 2 变量 怎么定义变量&#xff1f; 定义变量 第四课课后习题2&#xff1a; 来学习新的内容吧 1 输入 C中既然有了输出 那必然有输入 这时 我们就要用到 cin 函数啦 cin 用法与cout 相似 但却有一个差异 co…

Vue(十八):echarts地图省市区联动

效果 数据来源 https://datav.aliyun.com/portal/school/atlas/area_selector 接口请求地址 https://geo.datav.aliyun.com/areas_v3/bound/geojson?code100000_full 源码 样式 .map {width: 1000px;height: 700px; }布局 <template><div class"map">…

linux -网络编程-多线程并发服务器

目录 1.三次握手和四次挥手 2 滑动窗口 3 函数封装思想 4 高并发服务器 学习目标&#xff1a; 掌握三次握手建立连接过程掌握四次握手关闭连接的过程掌握滑动窗口的概念掌握错误处理函数封装实现多进程并发服务器实现多线程并发服务器 1.三次握手和四次挥手 思考: 为什么…

linux centos7 安装java17

删除旧版本的java或者说是自带的&#xff0c;免得干扰 查找java rpm -qa|grep java java-1.8.0-openjdk-1.8.0.262.b10-1.el7.x86_64 javapackages-tools-3.4.1-11.el7.noarch tzdata-java-2020a-1.el7.noarch python-javapackages-3.4.1-11.el7.noarch java-1.8.0-open…

力扣热门100题之矩阵置0【中等】

题目描述 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 示例 2&#xff…

❤️创意网页:萌翻少女心的果冻泡泡 - 创造生动有趣的视觉效果

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

计算机网络(Computer Networks)基础

本篇介绍计算机网络的基础知识。 文章目录 1. 计算机网络历史2. 以太网" (Ethernet)2.1 以太网" (Ethernet)的简单形式及概念2.2 指数退避解决冲突问题2.3 利用交换机减少同一载体中设备2.4 互联网&#xff08;The Internet&#xff09;2.5 路由(routing)2.6 数据包…

渗透测试基础知识(1)

渗透基础知识一 一、Web架构1、了解Web2、Web技术架构3、Web客户端技术4、Web服务端组成5、动态网站工作过程6、后端存储 二、HTTP协议1、HTTP协议解析2、HTTP协议3、http1.1与http2.0的区别4、HTTP协议 三、HTTP请求1、发起HTTP请求2、HTTP响应与请求-HTTP请求3、HTTP响应与请…

typedef对类型的重命名

typedef 重命名的类型 重命名后的类型名 typedef重命名函数指针类型时的特别写法 正确的重命名函数指针类型的方式 运用&#xff1a; 用typedef简化下面这个代码 简化后&#xff1a;

【C语言day11】

数据类型的等级从低到高如下&#xff1a;char int long float double运算的时候是从低转到高的&#xff0c;表达式的类型会自动提升或者转换为参与表达式求值的最上级类型 #include <stdio.h> int main() {int x -1;unsigned int y 2;if (x > y){printf("x is …

使用Express部署Vue项目

使用Express部署Vue项目 目录 1. 背景 2. 配置Vue CLI 1.1 安装nodejs 1.2 创建vue-cli 1.3 创建vue项目 1.4 构建vue项目3. 配置Express 2.1 安装express 2.2 创建项目4. 使用express部署vue项目 1&#xff0c;背景 我们想要做一个前后端分离的课程项目&#xff0c;前端…

nacos2.2.3最新版启动所遇到的问题总结

前言 有问题就看官方文档&#xff0c;看不懂或者还是报错再看博客&#xff01;因为有时候忙的焦头烂额&#xff0c;却发现官方写的非常清楚&#xff0c;而且人家还自带一个example示例&#xff0c;自己都没有看&#xff0c;自己瞎折腾&#xff01;本人吃过亏&#xff0c;特此提…