【动态规划】【广度优先搜索】LeetCode:2617 网格图中最少访问的格子数

本文涉及的基础知识点

二分查找算法合集
动态规划

题目

给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。
当你在格子 (i, j) 的时候,你可以移动到以下格子之一:
满足 j < k <= grid[i][j] + j 的格子 (i, k) (向右移动),或者
满足 i < k <= grid[i][j] + i 的格子 (k, j) (向下移动)。
请你返回到达 右下角 格子 (m - 1, n - 1) 需要经过的最少移动格子数,如果无法到达右下角格子,请你返回 -1 。
示例 1:
输入:grid = [[3,4,2,1],[4,2,3,1],[2,1,0,0],[2,4,0,0]]
在这里插入图片描述

输出:4
解释:上图展示了到达右下角格子经过的 4 个格子。
示例 2:
输入:grid = [[3,4,2,1],[4,2,1,1],[2,1,1,0],[3,4,1,0]]
输出:3
解释:上图展示了到达右下角格子经过的 3 个格子。
在这里插入图片描述

示例 3:
输入:grid = [[2,1,0],[1,0,0]]
输出:-1
解释:无法到达右下角格子。
参数范围
m == grid.length
n == grid[i].length
1 <= m, n <= 105
1 <= m * n <= 105
0 <= grid[i][j] < m * n
grid[m - 1][n - 1] == 0

广度优先搜索和二分查找

时间复杂度

O(mnlogmax(m,n))。遍历每个单格时间复杂度O(nm),处理一个单格O(n)+O(m)。暴力方法的时间复杂度O(nmk),极端情况下超时。

变量解析

vRows各行没有处理的单格的列号
vCols各列没有处理的单格行号
vDis各单格距离起点的距离
que需要处理邻居的单格

核心代码

class Solution {
public:
int minimumVisitedCells(vector<vector>& grid) {
m_r = grid.size();
m_c = grid.front().size();
vector<set> vRows(m_r), vCols(m_c);
for (int r = 0; r < m_r; r++)
{
for (int c = 0; c < m_c; c++)
{
if (r + c == 0)
{
continue;
}
vRows[r].emplace©;
vCols[c].emplace®;
}
}
vector vDis(m_c * m_r,-1);
vDis[0] = 1;
queue<pair<int, int>> que;
que.emplace(0, 0);
auto Do = [&](int iDis,const int r, const int c)
{
vDis[m_c * r + c] = iDis + 1;
que.emplace(r, c);
};
while (que.size())
{
const auto [r, c] = que.front();
que.pop();
const int len = grid[r][c];
const int dis = vDis[m_c * r + c];
{//右跳
auto it = vRows[r].lower_bound©;
auto ij = vRows[r].upper_bound(c + len);
for (auto tmp = it; tmp != ij; ++tmp)
{
Do(dis, r, *tmp);
vCols[*tmp].erase®;
}
vRows[r].erase(it, ij);
}
{
auto it = vCols[c].lower_bound®;
auto ij = vCols[c].upper_bound(r + len);
for (auto tmp = it; tmp != ij; ++tmp)
{
Do(dis, *tmp,c);
vRows[*tmp].erase©;
}
vCols[c].erase(it, ij);
}
}
return vDis.back();
}
int m_r, m_c;
};

测试用例

template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{if (v1.size() != v2.size()){assert(false);return;}for (int i = 0; i < v1.size(); i++){assert(v1[i] == v2[i]);}
}template<class T>
void Assert(const T& t1, const T& t2)
{assert(t1 == t2);
}int main()
{vector<vector<int>> grid;{Solution slu;grid = { {3,4,2,1},{4,2,3,1},{2,1,0,0},{2,4,0,0} };auto res = slu.minimumVisitedCells(grid);Assert(4, res);}{Solution slu;grid = { {3,4,2,1},{4,2,1,1},{2,1,1,0},{3,4,1,0} };auto res = slu.minimumVisitedCells(grid);Assert(3, res);}{Solution slu;grid = { {2,1,0},{1,0,0} };auto res = slu.minimumVisitedCells(grid);Assert(-1, res);}
}

动态规划

广度优先搜索是基于动态规划实现的,如果不修改广度优先的实现,无需突出动态规划。经典广度优先搜索时,先处理距离起点近的,再处理距离远点的。是为了保证动态规划的无后效性。通俗的说:就是每个运算的前提条件都已经计算完毕。距离为iDis的单格显然是距离iDis-1单格的邻居,计算iDis的单格时,显然要计算完所有距离为iDis-1的单格。本题只右移和下移,先行后列,行列都是从小到大,也可以保证无后效性。优化枚举顺序后,就不再是广度优先搜索了,变成的普通的动态规划。

时间复杂度

O(mnlogmax(n,m))。

变量解析

rowMinHeap当前行可以到达的列和总共经过的单格数-1
colMinHeaps各列可以到达的行和总共经过的单格数-1

用小根堆记录经过的单格数和列号。由于列号是增加的,所有如果堆顶的列号小于当前列号,则对应小于后面的列号,可以永久删除。 删除堆顶列号过小的元素后,堆顶元素就是最小经过的单格树。

代码

class Solution {
public:typedef priority_queue<pair<int,int>, vector<pair<int, int>>, greater<>> HTYPE;int minimumVisitedCells(vector<vector<int>>& grid) {m_r = grid.size();m_c = grid.front().size();vector<vector<int>> vDis(m_r, vector<int>(m_c, -1));		vector< HTYPE> colMinHeaps(m_c);for (int r = 0; r < m_r; r++){	HTYPE rowMinHeap;auto Add = [&](const int r, const int c, int iNewDis){vDis[r][c] = iNewDis;rowMinHeap.emplace(iNewDis, c + grid[r][c]);colMinHeaps[c].emplace(iNewDis, r + grid[r][c]);};for (int c = 0; c < m_c; c++){if (r + c == 0){Add(r, c, 1);continue;}while (rowMinHeap.size() && (rowMinHeap.top().second < c)){rowMinHeap.pop();}while (colMinHeaps[c].size() && (colMinHeaps[c].top().second < r )){colMinHeaps[c].pop();}int iPreMin = INT_MAX;if (rowMinHeap.size()){iPreMin = min(iPreMin, rowMinHeap.top().first);}if (colMinHeaps[c].size()){iPreMin = min(iPreMin, colMinHeaps[c].top().first);}if (INT_MAX == iPreMin){continue;}Add(r, c, iPreMin + 1);}}		return vDis.back().back();}int m_r, m_c;
};

单调向量(有序向量)

可以逆向考虑,从终点到起点。这样可以记录可以到达单元格的行(列)和经过的单格数。在保持数据的单调的情况下,行(列)递减,单格数递增。新增有利条件: 行(列)插入的顺序也递减。这意味者可以用单调向量。

代码

class Solution {
public:int minimumVisitedCells(vector<vector<int>>& grid) {m_r = grid.size();m_c = grid.front().size();vector<vector<int>> vDis(m_r, vector<int>(m_c, -1));vector< vector<pair<int,int>>> cols(m_c);//列(行)号按降序排除,距离按升序排列for (int r = m_r-1; r >= 0 ; r-- ){vector<pair<int, int>> row;auto Add = [&](const int r, const int c, int iNewDis){vDis[r][c] = iNewDis;while (row.size() && (row.back().first >= iNewDis)){row.pop_back();}row.emplace_back(iNewDis,c);while (cols[c].size() && (cols[c].back().first >= iNewDis)){cols[c].pop_back();}cols[c].emplace_back(iNewDis, r);};auto Cmp = [&](const pair<int, int>& pr, int rc){return pr.second > rc;};for (int c = m_c-1 ; c >= 0 ;c--){if (r + c + 2 == m_r+m_c ){Add(r, c, 1);continue;}				int iPreMin = INT_MAX;auto it = std::lower_bound(row.begin(), row.end(), c + grid[r][c], Cmp);if (row.end() != it ){iPreMin = min(iPreMin, it->first);}auto ij = std::lower_bound(cols[c].begin(), cols[c].end(), r + grid[r][c], Cmp);if (cols[c].end() != ij ){iPreMin = min(iPreMin, ij->first);}if (INT_MAX == iPreMin){continue;}Add(r, c, iPreMin + 1);}}return vDis.front().front();}int m_r, m_c;
};

2023年8月版

typedef std::priority_queue<std::pair<int, int>,vector<std::pair<int, int>>,std::greater<std::pair<int, int>> > QUE;
class Solution {
public:
int minimumVisitedCells(vector<vector>& grid) {
m_r = grid.size();
m_c = grid[0].size();
vector<vector> vVis(m_r, vector(m_c,INT_MAX));
vVis[0][0] = 1;
vector< std::multiset> setCols(m_c);
vector< QUE> vDelCols(m_c);
for (int r = 0; r < m_r; r++)
{
for (int c = 0; c < m_c; c++)
{
auto& setCol = setCols[c];
auto& vDelCol = vDelCols[c];
while (vDelCol.size() && (vDelCol.top().first == r))
{
setCol.erase(setCol.find(vDelCol.top().second));
vDelCol.pop();
}
}
std::multiset setRow;
QUE vDelRow;
auto Add = [&](int r, int c, int dis, int value)
{
if (INT_MAX == dis)
{
return;
}
setRow.emplace(dis);
vDelRow.emplace(c + value + 1, dis);
setCols[c].emplace(dis);
vDelCols[c].emplace(r + value + 1, dis);
};
for (int c = 0; c < m_c; c++)
{
if (r + c == 0)
{
Add(0, 0, vVis[0][0], grid[r][c]);
continue;
}
while (vDelRow.size() && (vDelRow.top().first == c))
{
setRow.erase(setRow.find(vDelRow.top().second));
vDelRow.pop();
}
if (setRow.size())
{
vVis[r][c] = min(vVis[r][c],*setRow.begin()+1);
}
auto& setCol = setCols[c];
if (setCol.size())
{
vVis[r][c] = min(vVis[r][c], *setCol.begin() + 1);
}
if (INT_MAX == vVis[r][c])
{
continue;
}
Add(r, c, vVis[r][c], grid[r][c]);
}
}
int iRet = vVis.back().back();
return (INT_MAX == iRet) ? -1 : iRet;
}
int m_r, m_c;
};

其它方法

可以用有向图并集查找,寻找没有删除的元素。r1和r2连接,表示[r1,r2)已经全部删除,直接处理r2。

2023年9月版

class Solution {
public:
int minimumVisitedCells(vector<vector>& grid) {
m_r = grid.size(), m_c = grid[0].size();
if (m_r * m_c == 1)
{
return 1;
}
vector<vector<std::pair<int,int>>> vvRowMinDis(m_c); // 每列的单调栈
int iRet = m_iNotMay;
for (int r = m_r - 1; r >= 0; r–)
{
std::vector<std::pair<int, int>> vColMinDis;//列号越来越小,值越来越大
for (int c = m_c - 1; c >= 0; c–)
{
auto& sta = vvRowMinDis[c];
if ((m_r - 1 == r) && (m_c - 1 == c))
{
vColMinDis.emplace_back(c, 1);
sta.emplace_back(r, 1);
continue;
}
int iCurDis = m_iNotMay;
//处理右移
auto it = std::lower_bound(vColMinDis.begin(), vColMinDis.end(), c + grid[r][c], [](const std::pair<int, int>& p1, int a)
{return p1.first > a; });
if (vColMinDis.end() != it)
{
const int iDis = it->second + 1;
iCurDis = min(iCurDis, iDis);
}
//处理左移
auto ij = std::lower_bound(sta.begin(), sta.end(), r + grid[r][c], [](const std::pair<int, int>& p1, int a)
{return p1.first > a; });
if (sta.end() != ij)
{
const int iDis = ij->second + 1;
iCurDis = min(iCurDis, iDis);
}
if (m_iNotMay == iCurDis)
{
continue;
}
while (sta.size() && (sta.back().second >= iCurDis))
{
sta.pop_back();
}
sta.emplace_back(r, iCurDis);
while (vColMinDis.size() && (vColMinDis.back().second >= iCurDis))
{
vColMinDis.pop_back();
}
vColMinDis.emplace_back(c, iCurDis);
if (r + c == 0)
{
iRet = iCurDis;
}
}
}
return (iRet >= m_iNotMay ) ? -1 : iRet;
}
int m_r, m_c;
const int m_iNotMay = 1000 * 1000 * 1000;

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关

下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

我想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

持续集成交付CICD:CentOS 7 安装SaltStack

目录 一、理论 1.SaltStack 二、实验 1.主机一安装master 2.主机二安装第一台minion 3.主机三安装第二台minion 4.测试SaltStack 三、问题 1.CentOS 8 如何安装SaltStack 一、理论 1.SaltStack &#xff08;1&#xff09;概念 SaltStack是基于python开发的一套C/S自…

Nginx 服务器安装及配置文件详解

1. 安装nginx 1.1 选择稳定版本 我们编译安装nginx来定制自己的模块&#xff0c;机器CentOS 6.2 x86_64。首先安装缺少的依赖包&#xff1a; # yum -y install gcc gcc-c make libtool zlib zlib-devel openssl openssl-devel pcre pcre-devel 这些软件包如果yum上没有的话…

一篇文章讲透TCP/IP协议

1 OSI 7层参考模型 2 实操连接百度 nc连接百度2次&#xff0c;使用命令netstat -natp查看就会重新连接一次百度 请求百度 3 三次握手、socket 应用层协议控制长连接和短连接 应用层协议->传输控制层&#xff08;TCP UDP&#xff09;->TCP&#xff08; 面向连接&am…

Numpy 实现C4.5决策树

C4.5 信息增益比实现决策树 信息增益比 g R ( D , A ) g ( D , A ) H ( D ) g_{R}(D, A)\frac{g(D, A)}{H(D)} gR​(D,A)H(D)g(D,A)​ 其中&#xff0c; g ( D , A ) g(D,A) g(D,A)是信息增益&#xff0c; H ( D ) H(D) H(D)是数据集 D D D的熵 代码实现 import numpy as …

SQLE 3.0 部署实践

来自 1024 活动的投稿系列 第一篇《SQLE 3.0 部署实践》 . 作者&#xff1a;张昇&#xff0c;河北东软软件有限公司高级软件工程师&#xff0c;腾讯云社区作者。 爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。 本文共 32…

Axure自定义元件

目录 1.processOne的使用 ​编辑2.自定义元件的使用、 2.1如何自定义一个元件 2.2使用自定义元件 导语&#xff1a; Axure是绘制原型图的软件&#xff0c;但是我们很多时候不知道&#xff0c;画哪一个板块&#xff0c;所以流程图的绘制也是非常重要的 1.processOne的使用…

Vue2.x源码:new Vue()做了啥

例子1new Vue做了啥?new Vue做了啥,源码解析 initMixin函数 初始化 – 初始化Vue实例的配置initLifecycle函数 – 初始化生命周期钩子函数initEvents – 初始化事件系统初始化渲染 initRender初始化inject选项 例子1 <div id"app"><div class"home&…

【Jenkins】节点 node、凭据 credentials、任务 job

一、节点 node Jenkins在安装并初始化完成后&#xff0c;会有一个主节点&#xff08;Master Node&#xff09;&#xff0c;默认情况下主节点可以同时运行的任务数是2&#xff0c;可以在节点配置中修改&#xff08;系统管理/节点和云管理&#xff09;。 Jenkins中的节点&#…

实战——Mac M2 安装mat工具

线上环境出现内存飙升的情况&#xff0c;需要工具定位问题发生点就需要用到mat工具了&#xff0c;之前都是在intel芯片环境上安装的&#xff0c;现在换了m2芯片&#xff0c;导致出现了问题&#xff0c;经过一系列调研都解决了&#xff0c;特此记录下&#xff0c;以备后查 开发…

Linux汇编语言编程-汇编语言

术语 Figure 3-13. 8086 Computer (Partial Model) reg 代表寄存器。 它可以是表 3.13 中列出的任何寄存器。 imm 代表立即数【immediate】&#xff08;可以理解为字面量&#xff0c;常量&#xff09;。 术语“立即数【immediate】”用于指代直接由十进制或十六进制表示形式给…

认识缓存,一文读懂Cookie,Session缓存机制。

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

SAP ABAP 面试题交流

1.列举AT事件并说明其作用&#xff0c;AT事件中的工作区有何不同&#xff1f; AT FIRST 循环loop中执行第一条数据 AT LAST 循环loop中执行最后一条数据 AT NEW 循环loop中指定字段&#xff08;包含指定字段&#xff09;记录与上一条记录不一致数据执行 AT END OF 循环loo…

2019年第八届数学建模国际赛小美赛D题安全选举的答案是什么解题全过程文档及程序

2019年第八届数学建模国际赛小美赛 D题 安全选举的答案是什么 原题再现&#xff1a; 随着美国进入一场关键性的选举&#xff0c;在确保投票系统的完整性方面进展甚微。2016年总统大选期间&#xff0c;唐纳德特朗普因被指控受到外国干涉而入主白宫&#xff0c;这一问题再次成为…

深度学习中的高斯分布

1 高斯分布数学表达 1.1 什么是高斯分布 高斯分布(Gaussian Distribution)又称正态分布(Normal Distribution)。高斯分布是一种重要的模型&#xff0c;其广泛应用与连续型随机变量的分布中&#xff0c;在数据分析领域中高斯分布占有重要地位。高斯分布是一个非常常见的连续概…

微信小程序uniapp记住密码

记住密码功能 在请求登录接口成功后&#xff0c;我们需要判断用户是否勾选记住密码&#xff0c;如果是&#xff0c;则将记住密码状态、账号信息存入本地。 下次登录时&#xff0c;获取本地的记住密码状态&#xff0c;如果为true则获取本地存储的账号信息&#xff0c;将信息回填…

ES中根据主键_id查询记录

一、需求 es中_type&#xff1a;_doc&#xff0c;想要根据主键_id查询记录 二、实现 复合查询中使用语句查询http://192.168.1.1/_doc/1

SpringSecurity6从入门到上天系列第八篇:SpringSecurity当中的默认登录页面是如何产生的?

&#x1f609;&#x1f609; 欢迎加入我们的学习交流群呀&#xff01; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring等等很多应用和源码级别的高质量视频和笔记资料&#xff0c;你想学的我们这里都…

龙迅LT2611UXC 双PORT LVDS转HDMI(2.0)+音频

描述&#xff1a; LT2611UXC是一个高性能的LVDS到HDMI2.0的转换器&#xff0c;用于STB&#xff0c;DVD应用程序。 LVDS输入可配置为单端口或双端口&#xff0c;有1个高速时钟通道&#xff0c;3~4个高速数据通道&#xff0c;最大运行1.2Gbps/通道&#xff0c;可支持高达9.6Gbp…

【BI】FineBI功能学习路径-20231211

FineBI功能学习路径 https://help.fanruan.com/finebi/doc-view-1757.html 编辑数据概述 1.1 调整数据结构 1.2 简化数据 2.1上下合并 2.2其他表添加列 2.3左右合并 新增分析指标 函数参考 https://help.fanruan.com/finereport/doc-view-1897.html 数值函数 日期函数 文…

利用vue-okr-tree实现飞书OKR对齐视图

vue-okr-tree-demo 因开发需求需要做一个类似飞书OKR对齐视图的功能&#xff0c;参考了两位大神的代码&#xff1a; 开源组件vue-okr-tree作者博客地址&#xff1a;http://t.csdnimg.cn/5gNfd 对组件二次封装的作者博客地址&#xff1a;http://t.csdnimg.cn/Tjaf0 开源组件v…