【二分查找】LeetCode1970:你能穿过矩阵的最后一天

本文涉及的基础知识点

二分查找算法合集

作者推荐

动态规划LeetCode2552:优化了6版的1324模式

题目

给你一个下标从 1 开始的二进制矩阵,其中 0 表示陆地,1 表示水域。同时给你 row 和 col 分别表示矩阵中行和列的数目。

一开始在第 0 天,整个 矩阵都是 陆地 。但每一天都会有一块新陆地被 水 淹没变成水域。给你一个下标从 1 开始的二维数组 cells ,其中 cells[i] = [ri, ci] 表示在第 i 天,第 ri 行 ci 列(下标都是从 1 开始)的陆地会变成 水域 (也就是 0 变成 1 )。

你想知道从矩阵最 上面 一行走到最 下面 一行,且只经过陆地格子的 最后一天 是哪一天。你可以从最上面一行的 任意 格子出发,到达最下面一行的 任意 格子。你只能沿着 四个 基本方向移动(也就是上下左右)。
请返回只经过陆地格子能从最 上面 一行走到最 下面 一行的 最后一天 。
示例 1:
输入:row = 2, col = 2, cells = [[1,1],[2,1],[1,2],[2,2]]
输出:2
解释:上图描述了矩阵从第 0 天开始是如何变化的。
可以从最上面一行到最下面一行的最后一天是第 2 天。
示例 2:
输入:row = 2, col = 2, cells = [[1,1],[1,2],[2,1],[2,2]]
输出:1
解释:上图描述了矩阵从第 0 天开始是如何变化的。
可以从最上面一行到最下面一行的最后一天是第 1 天。
示例 3:
输入:row = 3, col = 3, cells = [[1,2],[2,1],[3,3],[2,2],[1,1],[1,3],[2,3],[3,2],[3,1]]
输出:3
解释:上图描述了矩阵从第 0 天开始是如何变化的。
可以从最上面一行到最下面一行的最后一天是第 3 天。
参数范围
2 <= row, col <= 2 * 104
4 <= row * col <= 2 * 104
cells.length == row * col
1 <= ri <= row
1 <= ci <= col
cells 中的所有格子坐标都是 唯一 的。

二分查找分析

时间复杂度

O(nlogn) ,二分查找O(logn),并集查找O(n)。

分析

随着天数的增大,逐步从能过变成不能过。左闭右开空间。
并集查找的时候只需要连接左边和上边。第一行下移,就是第二行上移。

代码

核心代码

class Solution {
public:
int latestDayToCross(int row, int col, vector<vector>& cells) {
m_r = row;
m_c = col;
int left = 0, right = m_r * m_c ;
while (right - left > 1)
{
const auto mid = left + (right - left) / 2;
vector<vector> mat(row, vector(col));
for (int i = 0; i < mid; i++)
{
mat[cells[i][0]-1][cells[i][1]-1] = 1;
}
if (Can(mat))
{
left = mid;
}
else
{
right = mid;
}
}
return left;
}
bool Can(const vector<vector>& mat)
{
const int n = m_r*m_c;
CUnionFind uf(n + 2);
for (int r = 0; r < m_r; r++)
{
for (int c = 0; c < m_c; c++)
{
if (1 == mat[r][c])
{
continue;
}
if ((0 != r) && (0 == mat[r - 1][c]))
{
uf.Union(r * m_c + c, (r - 1) * m_c + c);
}
if ((0 != c) && (0 == mat[r][c - 1]))
{
uf.Union(r * m_c + c, r * m_c + c - 1);
}
}
}
for (int c = 0; c < m_c; c++)
{
if (0 == mat[0][c])
{
uf.Union(n, c);
}
if (0 == mat[m_r - 1][c])
{
uf.Union(n+1, (m_r - 1) * m_c + c);
}
}
return uf.IsConnect(n, n + 1);
}
int m_r,m_c;
};

测试用例

template
void Assert(const vector& v1, const vector& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
assert(v1[i] == v2[i]);
}
}

template
void Assert(const T& t1, const T& t2)
{
assert(t1 == t2);
}

int main()
{
int row, col, res;
vector<vector> cells;
{
row = 2, col = 2, cells = { {1,1},{2,1},{1,2},{2,2} };
Solution slu;
auto res = slu.latestDayToCross(row, col, cells);
Assert(2, res);
}
{
row = 2, col = 2, cells = { {1,1},{1,2},{2,1},{2,2} };
Solution slu;
auto res = slu.latestDayToCross(row, col, cells);
Assert(1, res);
}
{
row = 6, col = 2, cells = { {4, 2}, { 6,2 }, { 2,1 }, { 4,1 }, { 6,1 }, { 3,1 }, { 2,2 }, { 3,2 }, { 1,1 }, { 5,1 }, { 5,2 }, { 1,2 } };
Solution slu;
auto res = slu.latestDayToCross(row, col, cells);
Assert(3, res);
}

  //CConsole::Out(res);

}

逆序思考

反向思考:水慢慢变成陆地,最晚什么时候连通陆地。
时间复杂度😮(n)

代码

class CUnionFind
{
public:CUnionFind(int iSize) :m_vNodeToRegion(iSize){for (int i = 0; i < iSize; i++){m_vNodeToRegion[i] = i;}m_iConnetRegionCount = iSize;}int GetConnectRegionIndex(int iNode){int& iConnectNO = m_vNodeToRegion[iNode];if (iNode == iConnectNO){return iNode;}return iConnectNO = GetConnectRegionIndex(iConnectNO);}void Union(int iNode1, int iNode2){const int iConnectNO1 = GetConnectRegionIndex(iNode1);const int iConnectNO2 = GetConnectRegionIndex(iNode2);if (iConnectNO1 == iConnectNO2){return;}m_iConnetRegionCount--;if (iConnectNO1 > iConnectNO2){UnionConnect(iConnectNO1, iConnectNO2);}else{UnionConnect(iConnectNO2, iConnectNO1);}}bool IsConnect(int iNode1, int iNode2){return GetConnectRegionIndex(iNode1) == GetConnectRegionIndex(iNode2);}int GetConnetRegionCount()const{return m_iConnetRegionCount;}vector<int> GetNodeCountOfRegion()//各联通区域的节点数量{const int iNodeSize = m_vNodeToRegion.size();vector<int> vRet(iNodeSize);for (int i = 0; i < iNodeSize; i++){vRet[GetConnectRegionIndex(i)]++;}return vRet;}std::unordered_map<int, vector<int>> GetNodeOfRegion(){std::unordered_map<int, vector<int>> ret;const int iNodeSize = m_vNodeToRegion.size();for (int i = 0; i < iNodeSize; i++){ret[GetConnectRegionIndex(i)].emplace_back(i);}return ret;}
private:void UnionConnect(int iFrom, int iTo){m_vNodeToRegion[iFrom] = iTo;}vector<int> m_vNodeToRegion;//各点所在联通区域的索引,本联通区域任意一点的索引,为了增加可理解性,用最小索引int m_iConnetRegionCount;
};class Solution {
public:int latestDayToCross(int row, int col, vector<vector<int>>& cells) {m_r = row;m_c = col;const int n = m_r * m_c;CUnionFind uf(n + 2);for (int c = 0; c < m_c; c++){uf.Union(n, c);uf.Union(n + 1, (m_r - 1) * m_c + c);}vector<vector<int>> mat(row, vector<int>(col,1));for (int i = cells.size() - 1; i >= 0; i--){const int r = cells[i][0] - 1;const int c = cells[i][1] - 1;mat[r][c] = 0;auto Add = [&](const int rNew, const int cNew){if ((rNew < 0) || (rNew >= m_r)){return;}if ((cNew < 0) || (cNew >= m_c)){return;}if (0 == mat[rNew][cNew]){uf.Union(r * m_c + c, rNew * m_c + cNew);}};Add(r + 1, c);Add(r - 1, c);Add(r, c - 1);Add(r, c + 1);if (uf.IsConnect(n, n + 1)){return i ;}}		return 0;}int m_r,m_c;
};

2023年3月旧代码

class Solution {
public:
int latestDayToCross(int row, int col, vector<vector>& cells) {
m_r = row;
m_c = col;
int left = 0,r = cells.size()+1 ;
while (r > left + 1)
{
int iMid = left + (r - left) / 2;
vector<vector> mat(m_r, vector(m_c));
for (int i = 0; i < iMid; i++)
{
mat[cells[i][0]-1][cells[i][1]-1] = 1;
}
if (bfs(mat))
{
left = iMid;
}
else
{
r = iMid;
}
}
return left;
}
bool bfs(const vector<vector>& mat)
{
std::queue<std::pair<int, int>> queCanViste;
std::unordered_map<int, std::unordered_set> setDo;
for (int c = 0; c < m_c; c++)
{
Add(queCanViste, setDo, 0, c, mat);
}
while (queCanViste.size())
{
const int r = queCanViste.front().first;
const int c = queCanViste.front().second;
if (r == m_r - 1)
{
return true;
}
queCanViste.pop();
Add(queCanViste, setDo, r+1, c, mat);
Add(queCanViste, setDo, r-1, c, mat);
Add(queCanViste, setDo, r , c+1, mat);
Add(queCanViste, setDo, r, c-1, mat);
}
return false;
}
void Add(std::queue<std::pair<int, int>>& queCanViste, std::unordered_map<int, std::unordered_set>& setDo, int r, int c,const vector<vector>& mat)
{
if ((r < 0) || (r >= m_r))
{
return;
}
if ((c<0) || (c >=m_c))
{
return;
}
if (1 == mat[r][c])
{
return;
}
if (setDo[r].count©)
{
return;
}
queCanViste.emplace(r, c);
setDo[r].emplace©;
}
int m_r, m_c;
};

2023年9月旧代码

class Solution {
public:
int latestDayToCross(int row, int col, vector<vector>& cells) {
m_c = col;
int iMaskNum = row * col;
vector<vector> grid(row, vector(col));
for (auto& v : cells)
{
v[0]–;
v[1]–;
grid[v[0]][v[1]] = 1;
}
CUnionFind uf(iMaskNum+2);//增加两个特殊端点; iMaskNum 连接所有第0行 iMaskNum+1 连接多有最后一行
auto Add = [&row,this,&grid,&uf](int iMask, int r, int c)
{
if ((r < 0) || (r >= row))
{
return;
}
if ((c < 0) || (c >= m_c))
{
return;
}
if (1 == grid[r][c])
{
return;
}
uf.Union(iMask, Mask(r, c));
};
for (int r = 0; r < row; r++)
{
for (int c = 0; c < m_c; c++)
{
if (1 == grid[r][c])
{
continue;
}
const int mask = Mask(r, c);
Add(mask, r + 1, c);
Add(mask, r, c + 1);
}
}
for (int c = 0; c < m_c; c++)
{
uf.Union(iMaskNum, Mask(0, c));
uf.Union(iMaskNum+1, Mask(row-1, c));
}
for (int i = cells.size() - 1; i >= 0; i–)
{
if (uf.IsConnect(iMaskNum, iMaskNum+1))
{
return i+1;
}
const auto& v = cells[i];
grid[v[0]][v[1]] = 0;
const int mask = Mask(v[0], v[1]);
Add(mask, v[0] + 1, v[1]);
Add(mask, v[0], v[1] + 1);
Add(mask, v[0] - 1, v[1]);
Add(mask, v[0], v[1] - 1);
}
return 0;
}
inline int Mask(int r, int c)
{
return r * m_c + c;
}
int m_c;
};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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

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

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

相关文章

【开源】基于JAVA的大病保险管理系统

项目编号&#xff1a; S 031 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S031&#xff0c;文末获取源码。} 项目编号&#xff1a;S031&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统配置维护2.2 系统参保管理2.3 大…

uniapp是否可以用elementUI等前端UI库、使用步骤以及需要注意的问题

文章目录 uniapp是否可以用elementUI等前端UI库使用方法和步骤问题如何解决 uniapp是否可以用elementUI等前端UI库 在PC端开发uniapp&#xff0c;可以用elementUI&#xff0c;因为elementUI就是PC端的。 在使用uniapp&#xff0c;选择vue2.0时&#xff0c;实测可以用nodejs16的…

java企业财务管理系统springboot+jsp

1、基本内容 &#xff08;1&#xff09;搭建基础环境&#xff0c;下载JDK、开发工具eclipse/idea。 &#xff08;2&#xff09;通过HTML/CSS/JS搭建前端框架。 &#xff08;3&#xff09;下载MySql数据库&#xff0c;设计数据库表&#xff0c;用于存储系统数据。 &#xff08;4…

三、使用CRT连接三台虚拟机

目录 1、建立连接 2、参数配置 3、设置主题,颜色和仿真 1、建立连接

SQL server 2016安装

1、关系数据库的基本概念。 行&#xff1a;每行成为一条“记录”或“元组”&#xff0c;用于描述一个对象的信息。 列&#xff1a;每列称为一个“字段”或“属性”&#xff0c;用于描述对象的一个属性。 2、主键与外键。 主键&#xff1a;键&#xff0c;即关键字。主键由一个或…

Android--Jetpack--Lifecycle详解

富贵本无根&#xff0c;尽从勤里得 一&#xff0c;定义 Lifecycle 是一个具备宿主生命周期感知能力的组件。它持有组件&#xff08;Activity/Fragment&#xff09;生命周期状态信息&#xff0c;并且允许其观察者监听宿主生命周期状态变化。 顾名思义&#xff0c;Lifecycle的主…

TA-Lib学习研究笔记(八)——Momentum Indicators 上

TA-Lib学习研究笔记&#xff08;八&#xff09;——Momentum Indicators 上 Momentum Indicators 动量指标&#xff0c;是最重要的股票分析指标&#xff0c;能够通过数据量化分析价格、成交量&#xff0c;预测股票走势和强度&#xff0c;大部分指标都在股票软件中提供。 1. A…

水利安全监测方案——基于RTU200的解决方案

引言&#xff1a; 水资源是人类赖以生存的重要基础&#xff0c;对于保障水利系统安全运行以及应对自然灾害起着关键作用。为了实现水利安全监测的目标&#xff0c;我们提出了基于RTU200的解决方案。本方案将结合RTU200的可靠性、灵活性和高效性&#xff0c;为您打造一个全面的…

[笔记]dubbo发送接收

公司需要使用java技术栈接入一套自定义的通讯协议&#xff0c;所以参考下dubbo的实现原理。 consumer 主要使用ThreadlessExecutor实现全consumer的全双工通讯。consumer创建本次请求的requestId用于将response和request匹配。 然后分以下几步完成一次请求发送并接收结果&…

Elasticsearch分词器--空格分词器(whitespace analyzer)

介绍 文本分析&#xff0c;是将全文本转换为一系列单词的过程&#xff0c;也叫分词。analysis是通过analyzer(分词器)来实现的&#xff0c;可以使用Elasticearch内置的分词器&#xff0c;也可以自己去定制一些分词器。除了在数据写入时将词条进行转换&#xff0c;那么在查询的时…

Spring 多数据源搭建

目录 前言 正文 1.Druid 介绍和使用 2.其他多数据源解决方案 总结 前言 对于复杂的业务和项目&#xff0c;可能在一个单体项目中存在需要连接多个数据库的情况。这时&#xff0c;就会使用到多数据源&#xff0c;实际中遇到的可能性比较大。 正文 如果一个项目中需要连…

@2023 中国家居家具行业数字化转型分析与案例解读|商派徐礼昭

作者&#xff1a;徐礼昭&#xff08;商派市场负责人&#xff0c;重构零售实验室负责人&#xff09; 中国的家居家具行业面临着市场竞争激烈、消费者需求多变等诸多挑战。为了应对这些挑战&#xff0c;许多品牌企业开始探索数字化转型的道路&#xff0c;以提升竞争力并满足消费…

java游戏攻略资讯网站的设计与实现springboot+vue

游戏攻略网站分为管理员与用户两种角色。 管理员的功能包括登录&#xff0c;用户管理&#xff0c;游戏分类管理&#xff0c;游戏攻略管理&#xff0c;游戏资讯管理等。 登录功能&#xff1a;管理员需要登录进入系统后台。 用户管理&#xff1a;实现用户信息的查询&#xff0c;修…

掌握未来设计趋势,开启AutoCAD2020创新之旅!

随着科技的不断发展&#xff0c;设计行业也在迅速演进&#xff0c;为满足设计师们日益增长的需求&#xff0c;Autodesk AutoCAD2020应运而生。作为一款全球领先的设计软件&#xff0c;AutoCAD2020不仅拥有更加强大的功能和更高效的性能&#xff0c;还融入了一系列的智能化设计工…

nrm安装以及常用命令

做为开发者&#xff0c;我们经常会使用到淘宝镜像去安装一些包。基本上设置的都是cnpm这种。但是这一长串其实很难记住。这时&#xff0c;我们可以用nrm进行镜像的切换更为方便。 npm install -g cnpm --registryhttps://registry.npm.taobao.org 安装方法 npm install nrm …

网页开发 JS基础

目录 JS概述 基本语法 数据类型内置方法 DOM对象 查找标签 绑定事件 操作标签 jQuery 查找标签 绑定事件 操作标签 Ajax请求 数据接口 前后端分离 ajax的使用 JS概述 一门弱类型的编程语言,属于基于对象和基于原型的脚本语言. 1 直接编写<script>console…

香港专才计划(输入内地人才计划)申请条件?附官网和申请攻略、利弊!

香港专才计划&#xff08;输入内地人才计划&#xff09;申请条件&#xff1f;附官网和申请攻略、利弊&#xff01; 输入内地人才计划&#xff08;英语&#xff1a;Admission Scheme for Mainland Talents and Professionals (ASMTP)&#xff09;&#xff0c;俗称专才计划&#…

element中el-input限制只输入正整数或保留两位小数

文章目录 一、前言二、实现2.1、HTML2.2、只输入正整数2.3、只能输入数字或小数点 三、最后 一、前言 常见的el-input可能会限制用户只能输入正整数或保留两位小数&#xff0c;达到输入金额的需求&#xff0c;点击【跳转】访问el-input的官方文档 element-ui是有el-input-numb…

九章量子计算机:探索量子世界的革命性工具

九章量子计算机:探索量子世界的革命性工具 一、引言 九章量子计算机的推出,是近年来科技界最为引人瞩目的成就之一。这款基于量子力学的计算机,以其独特的计算方式和潜在的应用前景,引发了全球范围内的关注和讨论。本文将深入探讨九章量子计算机的原理、技术特点、应用前景…

HarmonyOS 开发案例分享:万能卡片也能用来玩游戏

一、前言 作为一名开发爱好者&#xff0c;从大了讲&#xff0c;我学习并进行 HarmonyOS 相关开发是为了能为鸿蒙生态建设尽一份绵薄之力&#xff0c;从小了讲&#xff0c;就是为了自己的兴趣。而万能卡片是一个让我非常感兴趣的东西。 很多时候我跟别人解释什么是万能卡片&…