BM61-矩阵最长递增路径

news2024/11/24 17:48:16

题目

给定一个 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),辅助矩阵的空间是一个二维数组。

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

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

相关文章

万字长文详解如何使用Swift提高代码质量 | 京东云技术团队

前言 京喜APP最早在2019年引入了Swift&#xff0c;使用Swift完成了第一个订单模块的开发。之后一年多我们持续在团队/公司内部推广和普及Swift&#xff0c;目前Swift已经支撑了70%以上的业务。通过使用Swift提高了团队内同学的开发效率&#xff0c;同时也带来了质量的提升&…

工服智能监测预警算法 yolov8

工服智能监测预警系统通过yolov8网络模型算法&#xff0c;工服智能监测预警算法对现场人员未按要求穿戴工服工装则输出报警信息&#xff0c;通知后台人员及时处理。Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测&#xff0c;核心思想就是利用整张图作为网络的输入&am…

Deepmotion: AI动作捕捉和3D身体追踪技术平台

【产品介绍】 Deepmotion是一家专注于使用AI技术进行动作捕捉和3D身体追踪的公司。自2014年成立以来&#xff0c;Deepmotion一直致力于让数字角色通过智能的动作技术变得栩栩如生。 Deepmotion提供了多种与动作数据相关的服务&#xff0c;其中最引人注目的是Animate 3D&#xf…

Java 如何在 Array 和 Set 之间进行转换

概述 在本文章中&#xff0c;我们对如何在 Java 中对 Array 和 Set 进行转换进行一些说明和示例。 这些示例通过使用 Core Java 和一些第三方的转换工具&#xff0c;例如 Guava 和 Apache Commons Collections。 更多有关的文章&#xff0c;请访问&#xff1a;Java - OSSEZ 相…

笔试强训--Day2

1.题目: 排序子序列 排序子序列https://www.nowcoder.com/questionTerminal/2d3f6ddd82da445d804c95db22dcc471?orderBy HotValue1&page1&onlyReferencefalse 【题目解析】&#xff1a; 本题要求解的是排序子序列&#xff0c;排序子序列为非递增或者非递减&#xff0…

空气炸锅CE认证/FCC认证/PSE认证/SAA认证以及METI备案

空气炸锅属于厨房小电器&#xff0c;出口到各个亚马逊站点的空气炸锅建议做如下认证&#xff1a; 美国站&#xff1a;FCC认证&#xff0c;UL测试&#xff0c;FDA测试&#xff0c;加州65测试等。 欧洲站&#xff1a;CE认证&#xff0c;RoHS认证。 德国站&#xff1a;GS认证。 …

【沐风老师】一步一步教你在3dMax中进行UVW贴图和展开UVW的方法

将简单或程序材质应用于对象并不难。但是当表面需要在其上显示某种纹理时&#xff0c;它会变得更加复杂。任何纹理贴图都放在材质的 Diffuse 插槽中&#xff0c;但渲染的结果可能无法预测。这就是为什么我们需要了解 3DMAX 如何将纹理应用于 3D 对象&#xff0c;什么是 UVW 贴图…

【STL】模拟实现vector(详解)

文章目录 前言vector的模拟实现一&#xff0c;搭建框架二&#xff0c;实现构造函数三&#xff0c;构造的其他方式传统写法1.拷贝构造2. 重载赋值操作符3. 使用迭代器构造4. 初始化为N个val的vector 现代写法1. 拷贝构造2. 赋值重载 四&#xff0c;实现vector相关函数1. reserve…

Linux centos安装Redis数据库并远程连接

目录 前言 1. Linux(centos8)安装redis数据库 2. 配置redis数据库 3. 内网穿透 3.1 安装cpolar内网穿透 3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址 4.1 保留一个固定tcp地址 4.2 配置固定TCP地址 4.3 使用固定的tcp地址连接 前言 Redis作为一款高速缓存的ke…

周杰伦官宣数智人“周同学”,数智人与数字人,不止一字之差

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 5月8日&#xff0c;周杰伦亮相中国移动元宇宙总部&#xff0c;与中国移动达成元宇宙领域系列合作&#xff0c;共同推出了数智人“周同学”。 周杰伦从 2003 年开始代言“动感地带”&#xff0c;可以说是中国移动 20 年的忠实…

6. 神经网络

6.1 非线性假设 假设有一个监督学习分类问题&#xff0c;训练集如图 如果利用logistic回归来解决这个问题&#xff0c;我们可以构造一个包含很多非线性项的logistic回归函数。 但在这个训练集只给出了两个特征&#xff0c;如果一旦特征变多了&#xff0c;多项式就会变得很多。…

1951-2023最新中国基础地理信息,包括水系、行政区、DEM高程、气象站经纬位置、土地利用,这些数据获取方法介绍

水系&#xff1a; 流域内所有河流、湖泊等各种水体组成的水网系统&#xff0c;称作水系。其中&#xff0c;水流最终流入海洋的称作外流水系&#xff0c;如太平洋水系、北冰洋水系&#xff1b;水流最终流入内陆湖泊或消失于荒漠之中的&#xff0c;称作内流水系。 [1] 流域面积的…

File 类和 InputStream, OutputStream 的用法

目录 1.文件系统操作 File 类 2.文件内容操作 InputStream 1.创建InputStream类对象 2.读文件 OutputStream 1.创建OutputStream类对象 2.写文件 上篇博客我们详情介绍了文件系统的相关知识。在文件系统操作中&#xff0c;由于文件是在硬盘上的&#xff0c;直接写代…

分布式系统入门概述

前言 随着互联网、物联网、人工智能等技术的广泛应用&#xff0c;计算机系统正在逐渐从单机转向网络化和分布式的趋势。那么&#xff0c;什么是分布式系统呢&#xff1f; 分布式概要 简而言之&#xff0c;分布式系统是由多个节点组成的&#xff0c;这些节点运行在不同的计算机上…

企业IDC服务器迁移上云视频教程来了(Windows和Linux)

上云是趋势&#xff0c;越来越多企业的IDC服务器选择迁移上云&#xff0c;迁移上云的方式有很多&#xff0c;阿里云提供服务器迁移中心SMC来帮助用户迁移上云。使用SMC服务器迁移中心&#xff0c;将您的源服务器方便快捷地迁移至阿里云&#xff0c;支持的迁移源类型包括IDC服务…

2 ElasticaSearch安装

2 ElasticaSearch安装 2.1 安装 安装配置&#xff1a; 1、新版本要求至少jdk1.8以上。 2、支持tar、zip、rpm等多种安装方式。 在windows下开发建议使用ZIP安装方式。 3、支持docker方式安装 详细参见&#xff1a;https://www.elastic.co/guide/en/elasticsearch/refere…

Unity大面积草地渲染——3、使用GPUInstancing渲染大面积的草

大家好&#xff0c;我是阿赵。 这里开始讲大面积草地渲染的第三个部分&#xff0c;使用GPU Instancing来渲染大面积的草。 一、在不使用GPU Instancing时的渲染情况 为了能看性能明显一点&#xff0c;我写了个工具&#xff0c;在10乘10的范围内生成了一万棵草。 由于我的电…

【Python】本地版 Whisper 自动转录器(附源码网址)

目 录 一、实时自动语音转录器简介 二、开源Whisper实时转录器 三、pyinstaller 打包发布exe应用程序四、修改版源代码 一、实时自动语音转录器简介 实时自动语音转录器是一种能够自动将语音信号转换为文字的应用程序。…

【机器学习】机器学习相关概念简述

一、什么是机器学习 机器学习指的是&#xff0c;在没有明确设置的情况下&#xff0c;使得计算机拥有自我学习能力的领域。 二、监督学习和无监督学习 2.1 监督学习 监督学习是指&#xff0c;我们给予算法一个数据集&#xff0c;其中的数据包含了若干个标签。一个例子就是给…

pdf怎么转换成jpg图片

pdf怎么转换成jpg图片&#xff1f;PDF格式可以在电脑和手机上使用&#xff0c;而且其内容不会被篡改。同时&#xff0c;PDF的通用兼容性较强&#xff0c;而且PDF文件操作简单&#xff0c;易于创作。PDF文件格式应用较为广泛。在我们创建PDF文件时&#xff0c;无论在何处查看数据…