LeetCode刷题笔记【30】:动态规划专题-2(不同路径、不同路径 II)

news/2024/6/16 10:02:22 标签: leetcode, 笔记, 动态规划, 算法, c++, 贪心算法

文章目录

  • 前置知识
  • 62.不同路径
    • 题目描述
    • 解题思路
    • 代码
  • 63. 不同路径 II
    • 题目描述
    • 障碍信息传递法(比较复杂)
    • 被障碍物阻挡后直接清空计数法(更简洁)
  • 总结

前置知识

参考前文

参考文章:
LeetCode刷题笔记【29】:动态规划专题-1(斐波那契数、爬楼梯、使用最小花费爬楼梯)

62.不同路径

题目描述

截图

LeetCode链接:https://leetcode.cn/problems/unique-paths/description/

解题思路

动态规划: 创建m×n的数组, 对应这个地图, 数组val表示有几种方法可以走到这一格
最开始, 第一行和第一列val都是1, 然后依次遍历更新val
每一格的val是其上和左格子的和

代码

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> map(m, vector<int>(n));
        for(int i=0; i<n; i++){
            map[0][i] = 1;
        }
        for(int i=0; i<m; i++){
            map[i][0] = 1;
        }
        for(int i=1; i<m; i++){
            for(int j=1; j<n; j++){
                map[i][j] = map[i-1][j] + map[i][j-1];
            }
        }
        return map[m-1][n-1];
    }
};

63. 不同路径 II

题目描述

截图

LeetCode链接:https://leetcode.cn/problems/unique-paths-ii/description/

障碍信息传递法(比较复杂)

参考<62. 不同路径>
动态规划, 先把石头初始化为INT_MAX(并且初始化过程中前面一个是INT_MAX, 那他自己也是INT_MAX)

递推遍历的过程中加一个判断
① 如果左和上都是INT_MAX, 那么本位置也是INT_MAX
② 如果上/左有一个是INT_MAX, 那么val是另一个非INT_MAX
③ 正常递推

class Solution {
private:
    int maxNum = INT_MAX;
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        for(int i=0; i<obstacleGrid.size(); ++i){
            for(int j=0; j<obstacleGrid[0].size(); ++j){
                if(obstacleGrid[i][j]==1)
                    obstacleGrid[i][j] = maxNum;
            }
        }

        if(obstacleGrid[0][0] != maxNum)
            obstacleGrid[0][0] = 1;
        for(int i=1; i<obstacleGrid[0].size(); ++i){
            if(obstacleGrid[0][i-1]==maxNum)
                obstacleGrid[0][i] = maxNum;
            if(obstacleGrid[0][i] != maxNum)
                obstacleGrid[0][i] = 1;
        }
        for(int i=1; i<obstacleGrid.size(); ++i){
            if(obstacleGrid[i-1][0]==maxNum)
                obstacleGrid[i][0] = maxNum;
            if(obstacleGrid[i][0] != maxNum)
                obstacleGrid[i][0] = 1;
        }

        for(int i=1; i<obstacleGrid.size(); ++i){
            for(int j=1; j<obstacleGrid[0].size(); ++j){
                if(obstacleGrid[i][j]==maxNum)
                    continue;
                int left = obstacleGrid[i-1][j];
                int over = obstacleGrid[i][j-1];
                if(left==maxNum && over==maxNum){
                    obstacleGrid[i][j] = maxNum;
                }else if(left==maxNum || over==maxNum){
                    obstacleGrid[i][j] = min(left, over);
                }else{
                    obstacleGrid[i][j] = left + over;
                }
            }
        }

        if(obstacleGrid.back().back()==maxNum)
            return 0;
        else
            return obstacleGrid.back().back();
    }
};

这样做相当于是如果在过程中遇到了障碍物, 就把这个障碍物的信息继续往后传递, 一直到遍历结束.

这样当然可以解决问题, 并且整个遍历的过程也非常符合手工推导的直觉.
但是落实到代码层面的话, 不管是初始化的过程, 推导的过程, 还是最后得出结果的步骤, 都会变得更加繁琐, 不够简洁.

被障碍物阻挡后直接清空计数法(更简洁)

另一种思路: 将obstacleGrid试做参考, 自己新建一个map;

在遍历过程中如果当前位置有障碍物, 那么就直接给当前位置赋值0(清空前面的累计计数);
其含义也可以理解为: 有0种方法可以走到当前位置.

在初始化时, 遇到障碍物, 直接停止初始化.

class Solution {
private:
    int maxNum = INT_MAX;
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)//一些trick, 起点终点处有障碍物就没法走了
            return 0;
        vector<vector<int>> map(m, vector<int>(n));
        for(int i=0; i<n; i++){
            if(obstacleGrid[0][i]==1)
                break;
            map[0][i] = 1;
        }
        for(int i=0; i<m; i++){
            if(obstacleGrid[i][0]==1)
                break;
            map[i][0] = 1;
        }
        for(int i=1; i<m; i++){
            for(int j=1; j<n; j++){
                if(obstacleGrid[i][j]==1)
                    continue;
                map[i][j] = map[i-1][j] + map[i][j-1];
            }
        }
        return map[m-1][n-1];
    }
};

总结

动态规划做起来真的比贪心舒服很多很多, 有逻辑的通畅感觉.

今天第二道题是第一道题的延伸拓展, 我虽然也做出来了, 但是用程序强行实现的手工推导思路, 并没有贴合dp数组的定义与实质.
导致算法不够简洁有力.
或许以后随着练习, 可以逐渐加强.

本文参考:
不同路径
不同路径 II


http://www.niftyadmin.cn/n/5011060.html

相关文章

直播预告 | 博睿学院 Bonree ONE接入zabbix数据源提高可观测运维能力

Zabbix是业界覆盖面非常普遍的监控工具。本课程将介绍目前公有云的基础监控体系的构建思路&#xff0c;讲述One产品对接Zabbix数据的必要性与可观测性赋能效果。 课程中会分享数据接入的过程&#xff0c;重点讲解zabbix工作机制&#xff0c;深入分析zabbix数据库表结构&#x…

基于眼动追踪的指挥控制系统人机交互效能评估方法

源自&#xff1a;航空兵器 作者&#xff1a;田晨智 宋敏 田继伟 邢清华 梁文洋 摘 要 关键词 指挥控制系统; 人机交互; 眼动; 熵权-变异系数法; NASA-TLX; SUS 引 言 1 基于眼动追踪的指控系统人机交互指标体系构建 1.1 指标体系 图1 指控系统人机交互效能评估指标体…

软件测试/测试开发丨Python Debug 调试与分析

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/26841 Python - Debug 调试与分析 程序调试 程序调试是将编制的程序投入实际运行前&#xff0c;用手工或编译程序等方法进行测试&#xff0c;修正【语法…

Linux 调试技术 Kprobe

目录 用途&#xff1a;一、技术背景1.1 kprobes的特点与使用限制1.2 kprobe原理 二、 基于kprobe探测模块的探测方式2.1、struct kprobe结构体2.2 kprobe API函数2.3 示例代码参考资料&#xff1a; 用途&#xff1a; 判断内核函数是否被调用&#xff0c;获取调用上下文、入参以…

debian11 安装 postgress 数据库 -- chatGPT

问&#xff1a;debian 安装 postgress 数据库 gpt: 要在Debian上安装PostgreSQL数据库&#xff0c;您可以按照以下步骤进行操作&#xff1a; 1. 打开终端&#xff1a;您可以通过点击"应用程序"菜单&#xff0c;然后在"系统工具"或"终端"下找到…

基于javaweb的网上图书销售系统(servlet+jsp)

系统简介 本项目采用eclipse工具开发&#xff0c;jspservletjquery技术编写&#xff0c;数据库采用的是mysql&#xff0c;navicat开发工具。 角色&#xff1a; 管理员普通用户 模块简介 管理员&#xff1a; 登录用户管理图书分类管理图书管理图书订单管理图书评论管理数据统…

动态住宅代理能使用在哪些场景

一、什么是动态住宅代理ip 动态住宅代理是一种代理技术&#xff0c;它利用代理服务器中转用户和目标服务器之间的网络流量&#xff0c;实现用户真实位置的屏蔽。代理提供商会有自己的ip大池子&#xff0c;当你通过代理服务器向网站发送请求时&#xff0c;服务器会从池子中选中…

过滤和分页源码、接口文档、jwt介绍和构成、base64编码、drf-jwt使用

过滤和分页源码 补充 #### 为什么在视图类中配置一个过滤类&#xff0c;就能走-filter_backends [SearchFilter,MyFilter]-GenericAPIView&#xff1a;继承APIVIew的视图类&#xff0c;是不能这样配置的----》自己过滤-filter_backends api_settings.DEFAULT_FILTER_BACKEN…