当前位置: 首页 > news >正文

BM61-矩阵最长递增路径

题目

给定一个 n 行 m 列矩阵 matrix ,矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。并输出这条最长路径的长度。

这个路径必须满足以下条件:

  1. 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。
  2. 你不能走重复的单元格。即每个格子最多只能走一次。

数据范围:1≤n,m≤1000,0≤matrix[i][j]≤1000。

进阶:空间复杂度 O(nm),时间复杂度 O(nm)。

例如:当输入为[[1,2,3],[4,5,6],[7,8,9]]时,对应的输出为5,其中的一条最长递增路径如下图所示:

示例1

输入:[[1,2,3],[4,5,6],[7,8,9]]

返回值:5

说明:1->2->3->6->9即可。当然这种递增路径不是唯一的。

示例2

输入:[[1,2],[4,3]]

返回值:4

说明: 1->2->3->4

备注:矩阵的长和宽均不大于1000,矩阵内每个数不大于1000。


思路1:深度优先搜索(dfs)(推荐使用)

深度优先搜索一般用于树或者图的遍历,其他有分支的(如二维矩阵)也适用。

它的原理是从初始点开始,一直沿着同一个分支遍历,直到该分支结束,然后回溯到上一级继续沿着一个分支走到底,如此往复,直到所有的节点都有被访问到。

既然是查找最长的递增路径长度,那我们首先要找到这个路径的起点,起点不好直接找到,就从上到下从左到右遍历矩阵的每个元素。然后以每个元素都可以作为起点查找它能到达的最长递增路径。

如何查找以某个点为起点的最长递增路径呢?我们可以考虑深度优先搜索,因为我们查找递增路径的时候,每次选中路径一个点,然后找到与该点相邻的递增位置,相当于进入这个相邻的点,继续查找递增路径,这就是递归的子问题。因此递归过程如下:

  • 终止条件: 进入路径最后一个点后,四个方向要么是矩阵边界,要么没有递增的位置,路径不能再增长,返回上一级。
  • 返回值: 每次返回的就是本级之后的子问题中查找到的路径长度加上本级的长度。
  • 本级任务: 每次进入一级子问题,先初始化后续路径长度为0,然后遍历四个方向(可以用数组表示,下标对数组元素的加减表示去往四个方向),进入符合不是边界且在递增的邻近位置作为子问题,查找子问题中的递增路径长度。因为有四个方向,所以最多有四种递增路径情况,因此要维护当级子问题的最大值。

具体做法:

  • step 1:使用一个dp数组记录i,j处的单元格拥有的最长递增路径,这样在递归过程中如果访问到就不需要重复访问。
  • step 2:遍历矩阵每个位置,都可以作为起点,并维护一个最大的路径长度的值。
  • step 3:对于每个起点,使用dfs查找最长的递增路径:只要下一个位置比当前的位置数字大,就可以深入,同时累加路径长度。

代码1

import java.util.*;public class Solution {//记录4个方向private int[][] dirs = new int[][] {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};private int n, m;public int solve (int[][] matrix) {//边界条件判断if (matrix.length == 0 || matrix[0].length == 0) {return 0;}int res = 0;n = matrix.length;m = matrix[0].length;//j,j处的单元格拥有的最长递增路径int[][] dp = new int[m + 1][n + 1];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {//更新最大值res = Math.max(res, dfs(matrix, dp, i, j));}}return res;}//深度优先搜索,返回最大单元格数public int dfs(int[][] matrix, int[][] dp, int i, int j) {if (dp[i][j] != 0) {return dp[i][j];}dp[i][j]++;for(int k = 0; k < 4; k++) {int nexti = i + dirs[k][0];int nextj = j + dirs[k][1];//判断下一个要遍历的位置是否满足条件:在矩阵范围内且满足路径递增if(nexti >= 0 && nexti < n && nextj >= 0 && nextj < m && matrix[nexti][nextj] > matrix[i][j]) {dp[i][j] = Math.max(dp[i][j], dfs(matrix, dp, nexti, nextj) + 1);}}return dp[i][j];}
}
  • 时间复杂度:O(mn),m、n分别为矩阵的两边,遍历整个矩阵以每个点作为起点,然后递归相当于遍历填充dp矩阵。
  • 空间复杂度:O(mn),辅助矩阵的空间是一个二维数组。

思路2:广度优先搜索(bfs)

广度优先搜索与深度优先搜索不同,它是将与某个节点直接相连的其它所有节点依次访问一次之后,再往更深处,进入与其他节点直接相连的节点。

bfs的时候我们常常会借助队列的先进先出,因为从某个节点出发,我们将与它直接相连的节点都加入队列,它们优先进入,则会优先弹出,在它们弹出的时候再将与它们直接相连的节点加入,由此就可以依次按层访问。

我们可以将矩阵看成是一个有向图,一个元素到另一个元素递增,代表有向图的箭头。这样我们可以根据有向图的出度入度找到最长的路径,且这个路径在矩阵中就是递增的。

具体做法:

  • step 1:计算每个节点(单元格)所对应的出度(符合边界条件且递增),对于作为边界条件的单元格,它的值比所有的相邻单元格的值都要大,因此作为边界条件的单元格的出度都为0。利用一个二维矩阵记录每个单元格的出度
  • step 2:利用拓扑排序的思想,从所有出度为0的单元格开始进行广度优先搜索。
  • step 3:借助队列来广度优先搜索,队列中每次加入出度为0的点,即路径最远点,每次从A点到B点,便将A点出度减一。
  • step 4:每次搜索都会遍历当前层的所有单元格,更新其余单元格的出度,并将出度变为0的单元格加入下一层搜索。
  • step 5:当搜索结束时,搜索的总层数即为矩阵中的最长递增路径的长度,因为bfs的层数就是路径增长的层数。

代码

import java.util.*;public class Solution {//记录4个方向private int[][] dirs = new int[][] {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};private int n, m;public int solve (int[][] matrix) {//边界条件判断if (matrix.length == 0 || matrix[0].length == 0) {return 0;}int res = 0;n = matrix.length;m = matrix[0].length;//j,j处的单元格拥有的最长递增路径int[][] outdegrees = new int[m + 1][n + 1];for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {for (int k = 0; k < 4; k++) {int nexti = i + dirs[k][0];int nextj = j + dirs[k][1];if (nexti >= 0 && nexti < n && nextj >= 0 && nextj < m &&matrix[nexti][nextj] > matrix[i][j]) {//符合条件,记录出度outdegrees[i][j]++;}}}}//辅助队列Queue<Integer> q1 = new LinkedList<Integer>();Queue<Integer> q2 = new LinkedList<Integer>();for (int i = 0; i < n; i++) {for (int j = 0; j < m; j++) {if (outdegrees[i][j] == 0) {//找到出度为0的入队q1.offer(i);q2.offer(j);}}}while (!q1.isEmpty()) {res++;int size = q1.size();for (int x = 0; x < size; x++) {int i = q1.poll();int j = q2.poll();//四个方向for (int k = 0; k < 4; k++) {int nexti = i + dirs[k][0];int nextj = j + dirs[k][1];//逆向搜索,所以下一步是小于if (nexti >= 0 && nexti < n && nextj >= 0 && nextj < m &&matrix[nexti][nextj] < matrix[i][j]) {//符合条件,出度递减outdegrees[nexti][nextj]--;if (outdegrees[nexti][nextj] == 0) {q1.offer(nexti);q2.offer(nextj);}}}}}return res;}
}
  • 时间复杂度:O(mn),m、n分别为矩阵的两边,相当于遍历整个矩阵两次。
  • 空间复杂度:O(mn),辅助矩阵的空间是一个二维数组。

相关文章:

BM61-矩阵最长递增路径

题目 给定一个 n 行 m 列矩阵 matrix &#xff0c;矩阵内所有数均为非负整数。 你需要在矩阵中找到一条最长路径&#xff0c;使这条路径上的元素是递增的。并输出这条最长路径的长度。 这个路径必须满足以下条件&#xff1a; 对于每个单元格&#xff0c;你可以往上&#xff…...

selenium——unittest框架

目录 一、unittest框架基本介绍二、unittest框架解析三、unittest框架使用方法1.测试固件2.测试套件3.用例的执行顺序4.忽略测试用例中的方法5.unittest断言6.HTML报告生成 一、unittest框架基本介绍 在进行selenium IDE脚本录制导出的脚本中&#xff0c;我们发现其中多了很多…...

matlab频谱分析详解

频谱分析是一种用于分析信号频率特征的方法&#xff0c;常用于信号处理、音乐分析、谐波产生等领域。MATLAB是一种功能强大的数字信号处理软件&#xff0c;提供了许多用于频谱分析的函数和工具箱。 本文将介绍如何使用MATLAB进行频谱分析&#xff0c;包括信号预处理、选择合适…...

用layui写用户登录页面遇到的问题

用layui写用户登录页面遇到的问题 1.在layui-row下面的layui-col-md还是换行 原因&#xff1a;link标签和script标签中的type属性没写&#xff0c;导致应该是script或者这个css没有识别出来 解决办法&#xff1a;link标签里面加上type为text/css, script标签中加上type为 2…...

NMOS双向转换电路实测以及上升沿尖峰处理

NMOS双向转换电路实测以及上升沿尖峰处理 NMOS双向转换电路 &#x1f527;采用的是5V供电的STC8H单片机输出PWM波形&#xff0c;经过上面的电平转换电路测量低压端的波形。 ✨在做3.3V <>5V 电平转换电路方案验证时&#xff0c;输入5V PWM波形和输出波形的波形上升沿有尖…...

【数据结构】选择排序(详细)

选择排序 1. 直接选择排序2. 堆排序2.1 堆2.2 堆的实现&#xff08;以大根堆为例&#xff09;2.3 堆排序 3. 堆排序&#xff08;topK问题&#xff09; 1. 直接选择排序 思想 以排升序为例。以a[i]为最大值&#xff08;或最小值&#xff09;&#xff0c;从a[i1]到a[n-1-i]比较选…...

什么是企业内容管理?

为什么出现企业内容管理&#xff1f; 在数字经济的宏观背景下&#xff0c;企业建立了各种应用系统以满足企业各业务的管理需求&#xff0c;这些系统每天都在产生大量的数据和信息资源&#xff0c;但在企业实践中存在很多数据或资源无法被应用系统获取、处理和共享。 比如发票…...

机器学习:分类、回归、决策树

分类&#xff1a;具有明确的类别 如&#xff1a;去银行借钱&#xff0c;会有借或者不借的两种类别 回归&#xff1a;不具有明确的类别和数值 如&#xff1a;去银行借钱&#xff0c;预测银行会借给我多少钱&#xff0c;如&#xff1a;1~100000之间的一个数值 不纯度&#xff1…...

java常见的异常,下一篇写如何正确处理异常

当我们编写Java程序时&#xff0c;经常会遇到各种异常情况。异常是指在程序执行过程中发生的一些错误或意外情况&#xff0c;它会打断程序的正常执行流程&#xff0c;并且需要被适当地处理。在Java中&#xff0c;异常被分为两种类型&#xff1a;可检查异常&#xff08;Checked …...

C#开发的OpenRA游戏之网络协议打包和解包

C#开发的OpenRA游戏之网络协议打包和解包 OpenRA游戏里,由于这是一个网络游戏,那么与服务器通讯就缺少不了, 既然要通讯,那么就需要协议,有协议就需要对数据进行打包和解包, 这个过程其实就是序列化与反序列化的过程。 游戏里很多命令都需要发送给服务器,以便服务器同…...

K8S通过Ansible安装集群

K8S通过Ansible安装集群 K8S集群安装可参考https://gitee.com/open-hand/kubeadm-ha.git、https://github.com/easzlab/kubeasz.git 安装高可用集群 git clone https://gitee.com/open-hand/kubeadm-ha.git && cd kubeadm-ha升级内核,非必需&#xff0c;默认不升级&…...

ChatGPT辩证观点:“人才不是一个企业的核心竞争力,对人才的管理能力才是一个企业的核心竞争力”

一、问&#xff1a; “人才不是一个企业的核心竞争力&#xff0c;对人才的管理能力才是一个企业的核心竞争力”这句话的理解和误解&#xff0c;这句话有哪个中心论点转移和变化 二、ChatGPT答&#xff1a; 这句话的理解和误解&#xff1a; 理解&#xff1a;这句话的意思是说…...

windows11 永久关闭windows defender的方法

1、按键盘上的windows按键&#xff0c;再点【设置】选项。 2、点击左侧菜单的【隐私和安全性】&#xff0c;再点击列表的【Windows安全中心】选项。 3、点击界面的【病毒和威胁保护】设置项。 4、病毒保护的全部关闭 5、别人的图&#xff08;正常是都开着的&#xff09; 6、终极…...

继承的基本知识

概念 假设基于A类&#xff0c;创建了B类&#xff0c;那么称A为B的父类&#xff0c;B为A的子类 子类会继承父类的成员变量及成员函数&#xff0c;但是不能继承构造、析构、运算符重载 假设又基于B创建了C&#xff0c;那么称B为C的直接基类&#xff0c;A为C的间接基类 继承按…...

【Frida-实战】EA游戏平台的文件监控(PsExec.exe提权)

▒ 目录 ▒ &#x1f6eb; 问题描述环境 1️⃣ 代码编写开源代码搜索自己撸代码procexp确定句柄对应的文件名并过滤 2️⃣ PsExec.exe提权定位找不到EABackgroundService.exe的问题 PsExec.exe提权PsExec.exe原理 &#x1f6ec; 结论&#x1f4d6; 参考资料 &#x1f6eb; 问题…...

可视化和回归分析星巴克咖啡在中国的定价建议

可视化和回归分析星巴克咖啡在中国的定价建议。星巴克的拿铁大杯Tall 在各国的价格。 Claude AI | 代码自动生成的数据可视化代码 选择Claude AI 而非 ChatGPT的理由是前者更懂中文​&#xff01;具体可以参见我前面的两篇文章对比两者的中英文翻译的表现及使用安装等难易程度​…...

热门影片怎么买票比较便宜,低价买电影票的方法,纯攻略!

有时候真的有被自己蠢到&#xff01;看电影看了这么多年&#xff0c;竟然不知道电影票价格才9.9元、19.9元就能买到。之前我看电影动不动就是几十上百块&#xff0c;感觉好亏啊。 其实&#xff0c;我也不敢相信的&#xff0c;通过这些平台&#xff0c;同时在节假日甚至春节档期…...

Python通过SWIG调用C++时出现的ImportError问题解析

摘要 win10系统&#xff0c;编译器为mingw&#xff0c;按照教程封装C的一个类并用python调用&#xff0c;一步步进行直到最后一步运行python代码时&#xff0c;在python代码中import example时报错ImportError: DLL load failed while importing _example: The specified modul…...

3ds Max云渲染有多快,3ds Max云渲染怎么用?

本地渲染效果图和动画3D项目是一个非常耗时的过程&#xff0c;当在场景中使用未优化的几何体或在最终渲染中使用大量多边形模型时&#xff0c;诸如此类的变量最终会增加渲染项目所需的时间和处理器能力。随着提供的渲染服务的云渲染平台出现&#xff0c;越来越多动画师、艺术家…...

Java之线程安全

目录 一.上节回顾 1.Thread类常见的属性 2.Thread类中的方法 二.多线程带来的风险 1.观察线程不安全的现象 三.造成线程不安全现象的原因 1.多个线程修改了同一个共享变量 2.线程是抢占式执行的 3.原子性 4.内存可见性 5.有序性 四.解决线程不安全问题 ---synchroni…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

大数据学习(132)-HIve数据分析

​​​​&#x1f34b;&#x1f34b;大数据学习&#x1f34b;&#x1f34b; &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 用力所能及&#xff0c;改变世界。 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!

本文介绍了一种名为AnomalyAny的创新框架&#xff0c;该方法利用Stable Diffusion的强大生成能力&#xff0c;仅需单个正常样本和文本描述&#xff0c;即可生成逼真且多样化的异常样本&#xff0c;有效解决了视觉异常检测中异常样本稀缺的难题&#xff0c;为工业质检、医疗影像…...

倒装芯片凸点成型工艺

UBM&#xff08;Under Bump Metallization&#xff09;与Bump&#xff08;焊球&#xff09;形成工艺流程。我们可以将整张流程图分为三大阶段来理解&#xff1a; &#x1f527; 一、UBM&#xff08;Under Bump Metallization&#xff09;工艺流程&#xff08;黄色区域&#xff…...

数据库正常,但后端收不到数据原因及解决

从代码和日志来看&#xff0c;后端SQL查询确实返回了数据&#xff0c;但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离&#xff0c;并且ai辅助开发的时候&#xff0c;很容易出现前后端变量名不一致情况&#xff0c;还不报错&#xff0c;只是单…...

写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里

写一个shell脚本&#xff0c;把局域网内&#xff0c;把能ping通的IP和不能ping通的IP分类&#xff0c;并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...