矩阵中的最长递增路径(困难)/dfs/动态规划

最长递增路径(dfs) 题目描述 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外。 输入 测试样例由多组测试数据组成。每组测试数据第一行输入两个正整数n,m代表矩形的行数和列数( 1 <= n,m <= 500 ) 接下来输入n * m 个数,每个数均在int范围内。 输出 输出矩阵中的最长递增路径长度。 这题是... 阅读详情

https://leetcode-cn.com/problems/longest-increasing-path-in-a-matrix/solution/ju-zhen-zhong-de-zui-chang-di-zeng-lu-jing-by-leet/
给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。


在这里插入图片描述
在这里插入图片描述


方法一:朴素的优先深度搜索
直觉
深度优先搜索可以找到任何单元格开始的最长递增路径.我们可以对全部单元格进行深度优先搜索

算法
每个单元格可以看作图G中的一个定点.若相邻两细胞的值满足 a < b,则存在有向边(a,b):
问题转换成:寻找有向图G中的最长路径
很显然我们可以使用深度优先搜索或广度优先搜索从根开始访问所有的连接细胞.在搜索期间更新路径的最大长度,并在搜索完成后得到答案.一般而言,在深度优先搜索中,我们可以使用集合visited来避免重复访问

class Solution4{
    private static final int[][] dir = {{0,1},{1,0},{0,-1},{-1,0}};
    private static int m;
    private static int n;

    public static int longestIncreasingPath(int[][] matrix) {
        if (matrix.length == 0){
            return 0;
        }
        m = matrix.length;
        n = matrix[0].length;
        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ans = Math.max(ans,def(matrix,i,j));
            }
        }
        return ans;
    }
    private static int def(int[][] matrix,int i,int j){
        int ans = 0;
        for (int[] d : dir){
            int x = i + d[0];
            int y = j + d[1];
            if (x >= 0 && y >= 0 && x < m && y < n && matrix[x][y] > matrix[i][j]){
                ans = Math.max(ans,def(matrix,x,y));
            }
        }
        return ++ans;
    }

    public static void main(String[] args) {
        int[][] arr = {
                {3,4,5},
                {3,3,6},
                {1,2,1}
        };
        System.out.println(longestIncreasingPath(arr));
    }
}

时间复杂度 :O(2^{m+n})。对每个有效递增路径均进行搜索
空间复杂度 : O(mn)。 对于每次深度优先搜索,系统栈需要 O(h) 空间,其中 h 为递归的最深深度.


方法二:记忆化深度优先搜索
直觉:
将递归的结果存储下来,这样每个子问题只需要计算一次

算法
从上面的分析中,我们知道在淳朴的深度优先搜索方法中有许多重复的计算
一个优化途径是我们可以用一个集合来避免一次深度优先搜索中的重复访问.该优化可以将一次深度优先搜索的时间复杂度优化到 O(mn)
下面介绍一个更有力的优化方法,记忆化

在计算中,记忆化是一种优化技术,它通过存储"昂贵"的函数调用的结果,在相同的输入再次出现时返回缓存的结果,以此加快程序速度

在本问题中,我们多次递归调用 dfs(x,y).但是,如果我们已经知道四个相邻单元格的结果,就只需要常数时间.在搜索过程中,如果未计算过单元格的结果,我们会计算并将其缓存;否则,直接从缓存中取

public class Solution {
  private static final int[][] dirs = {{0,1},{1,0},{0,-1},{-1,0}};
    private static int m,n;

    public static int longestIncreasingPath(int[][] matrix) {
        if (matrix.length == 0){
            return 0;
        }
        m = matrix.length;
        n = matrix[0].length;
        int[][] cache = new int[m][n];//这个就是缓存??
        int ans = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                ans = Math.max(ans,dfs(matrix,i,j,cache));
            }
        }
        return ans;
    }
    public static int dfs(int[][] matrix,int i,int j,int[][] cache){
        if (cache[i][j] != 0){
            return cache[i][j];
        }
        for (int[] d : dirs){
            int x = i + d[0];
            int y = j + d[1];
            if (0 <= x && x < m && 0 <= y && y < n && matrix[x][y] > matrix[i][j]){
                cache[i][j] = Math.max(cache[i][j],dfs(matrix,x,y,cache));
            }
        }
        return ++cache[i][j];
    }
}

在这里插入图片描述
时间复杂度:
O(mn).每个单元格均计算一次,且只被计算一次.每条边也均计算一次并只计算一次.总时间复杂度为O(V+E).V是顶点总数,E是边总数.本问题中,O(V) = O(mn) ,O(E) = O(4V) = O(mn)
空间复杂度:
O(mn).缓存决定了空间复杂度


方法三:剥洋葱(动态规划)
直觉
每个细胞的结果只与相邻的结果有关,能否使用动态规划呢?

算法
如果我们定义从单元格(i,j)开始的最长递增路径为函数
f(i,j)
则可以写出状态转移函数
f(i,j) = max{f(x,y)|(x,y) is a nei *** or of (i,j) and matrix[x][y] > matrix[i][j] } + 1;
此公式与以前方法使用的公式相同.有了状态转移函数,你可能会觉得可以使用动态规划来推导出所有结果,取他的深度优先搜索!
这听起来很美好,可惜忽略了一件事情:我们没有依赖列表
想要让动态规划有效,如果问题B依赖问题A的结果,就必须确保问题A比问题B先计算.这样的依赖顺序对许多问题十分简单自然.如著名的斐波那契数列:
F(0) = 1,F(1) = 1,F(n) = F(n -1 ) + F(n - 2)
子问题F(n)依赖于F(n -1 ) + F(n - 2).因此,自然顺序就是正确的计算顺序.被依赖者总会先被计算
这种依赖顺序的术语是"拓扑顺序"或"拓扑排序"

对有向无环图的拓扑排序是顶点的一个线性排序,使得对于任何有向边(u,v),顶点u都在顶点v的前面

在本问题中,拓扑顺序并不简单自然.没有矩阵的值,我们无法知道两个邻居A和B的依赖关系.作为预处理,我们必须显示执行拓扑排序.之后我们可以按照存储的拓扑顺序使用状态转移函数动态地解决问题

有多种实现拓扑排序的方法.这里我们使用的是一种被称为"剥洋葱"的方法.其思路是在一个有向无环图中,会有一些不依赖于其他顶点的的顶点,称为"叶子".我们将这些叶子放于一个列表中(他们的内部排序并不重要),然后将他们从图中移出.移除之后就会产生新的叶子,就像一层一层拨开洋葱的心.最后,列表中就会存储有效的拓扑排序.
在本问题中,因为我们想要求出整个图的最长路径,也就是洋葱层的总层数.因此我们可以在剥离的期间计算层数,在不调用动态规划的情况下返回计数.

// Topological Sort Based Solution
// An Alternative Solution
public class Solution {
    private static final int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
    private int m, n;
    public int longestIncreasingPath(int[][] grid) {
        int m = grid.length;
        if (m == 0) return 0;
        int n = grid[0].length;
        // padding the matrix with zero as boundaries
        // assuming all positive integer, otherwise use INT_MIN as boundaries
        int[][] matrix = new int[m + 2][n + 2];
        for (int i = 0; i < m; ++i)
            System.arraycopy(grid[i], 0, matrix[i + 1], 1, n);

        // calculate outdegrees
        int[][] outdegree = new int[m + 2][n + 2];
        for (int i = 1; i <= m; ++i)
            for (int j = 1; j <= n; ++j)
                for (int[] d: dir)
                    if (matrix[i][j] < matrix[i + d[0]][j + d[1]])
                        outdegree[i][j]++;

        // find leaves who have zero out degree as the initial level
        n += 2;
        m += 2;
        List<int[]> leaves = new ArrayList<>();
        for (int i = 1; i < m - 1; ++i)
            for (int j = 1; j < n - 1; ++j)
                if (outdegree[i][j] == 0) leaves.add(new int[]{i, j});

        // remove leaves level by level in topological order
        int height = 0;
        while (!leaves.isEmpty()) {
            height++;
            List<int[]> newLeaves = new ArrayList<>();
            for (int[] node : leaves) {
                for (int[] d:dir) {
                    int x = node[0] + d[0], y = node[1] + d[1];
                    if (matrix[node[0]][node[1]] > matrix[x][y])
                        if (--outdegree[x][y] == 0)
                            newLeaves.add(new int[]{x, y});
                }
            }
            leaves = newLeaves;
        }
        return height;
    }
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/longest-increasing-path-in-a-matrix/solution/ju-zhen-zhong-de-zui-chang-di-zeng-lu-jing-by-leet/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
LeetCode-329:矩阵中的最长递增路径(动态规划) 原题 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: **输入: ** nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 **解释: ** 最长递增路径为 [1, 2, 6, 9]。 示例 2: 输入: nums = [ [3,4,5], ... 阅读详情

相关推荐

【leetcode】329 矩阵中的最长递增路径(动态规划)

题目链接:https://leetcode-cn.com/problems/longest-increasing-path-in-a-matrix/ 【hard】 题目描述 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6...

zjwreal 1718

每日一题——矩阵最长递增路径

每日一题——矩阵最长递增路径

zxjiaya的博客 2101

[算法题解详细]DFS解力扣329矩阵中的最长递增路径,web开发问题

整理面试题,不是让大家去只刷面试题,而是熟悉目前实际面试中常见的考察方式和知识点,做到心中有数,也可以用来自查及完善知识体系。《前端基础面试题》,《前端校招面试题精编解析大全》,《前端面试题宝典》,《前端面试题:常用算法》PDF完整版点击这里免费领取大家去只刷面试题,而是熟悉目前实际面试中常见的考察方式和知识点,做到心中有数,也可以用来自查及完善知识体系。《前端基础面试题》,《前端校招面试题精编解析大全》,《前端面试题宝典》,《前端面试题:常用算法》PDF完整版点击这里免费领取。

2401_84092809的博客 910

leetcode 矩阵中的最长递增路径(Java)

Leetcode汇总贴: leetcode经典编程题目(Java实现) leetcode题目 矩阵中的最长递增路径 -- leetcode 329 题目描述 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6,6,...

聚沙成塔 1892

算法题:矩阵的最长递增路径

题目 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 解释: 最长递增路径为 [1, 2, 6, 9] 示例 2: 输入: nums = [ [3,...

一条菜狗的升级之路 952

LeetCode 高级 - 矩阵中的最长递增路径

矩阵中的最长递增路径 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 解释: 最长递增路径为 [1, 2, 6, 9]。 示例 2: 输入: nums ...

whdAlive 's blog 9393

[Lc6_记忆化搜索] 最长递增子序列 | 矩阵中的最长递增路径

  int ret=1;  //在for前初始化     //!!!!! 必须等于1,如果pos是最后一个,循环进不去,但它也是一个子序列,返回1才对.  return 通过Max选择  返回该点最长路径 主函数也要for

No Game No Life 1544

矩阵中的最长递增路径

矩阵中的最长递增路径 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 解释: 最长递增路径为 [1, 2, 6, 9]。 示例 2: 输入: nums = [ ...

wolfGuiDao的博客 1516

[算法题解详细]DFS解力扣329矩阵中的最长递增路径

总结来说,面试成功=基础知识+项目经验+表达技巧+运气。我们无法控制运气,但是我们可以在别的地方花更多时间,每个环节都提前做好准备。面试一方面是为了找到工作,升职加薪,另一方面也是对于自我能力的考察。能够面试成功不仅仅是来自面试前的临时抱佛脚,更重要的是在平时学习和工作中不断积累和坚持,把每个知识点、每一次项目开发、每次遇到的难点知识,做好积累,实践和总结。点击这里领取Web前端开发经典面试题总结来说,面试成功=基础知识+项目经验+表达技巧+运气。

2401_84092832的博客 1107

微机原理与接口技术第六版周荷琴课后习题答案及解析(期末重点)

第二章 1.8086/8088CPU可直接寻址多少个内存(字节)单元?多少个I/O端口?它们的外部数据总线各有多少根? 8086/8088CPU使用的是20位地址总线,因此可以直接寻址2的20次方个内存单元(即1,048,576),每个单元大小为1字节,因此可以直接寻址2的20次方个字节的内存。 8086/8088CPU有64K个I/O端口,每个端口占用1字节的地址空间,因此可以直接寻址6...

Wmicheng的博客 1万+

保姆级教程:用YOLOv11和CRNN从零搭建一个车牌识别系统(附完整代码)

本文提供了一份详细的YOLOv11和CRNN实战指南,教你从零搭建高精度车牌识别系统。内容包括环境配置、数据集处理、模型训练与优化,以及系统集成与性能优化技巧,附完整代码实现。特别介绍了YOLOv11在车牌检测中的创新应用和CRNN在车牌识别中的关键技术。

weixin_28358083的博客 320

牛客热题:矩阵最长递增路径

​ dfs的时间复杂度为O(m * n), 主函数调用了m * n次,所以总体的时间复杂度是O(​ 对于每个(x, y)来说,遍历它上下左右四个坐标,查看是否越界或者满足递增的要求;创建了一个和原矩阵空间大小相同的矩阵用于判断当前的左边是否被递归过,以及一些变量。创建了一个和原矩阵同等空间的dp数组,则空间复杂度为O(n * m)​ 所以总体上来说空间复杂度:O(n * m);​ 相当于遍历一遍对应的矩阵O(n * m)​ 若是满足要求就继续递归满足要求的点。: 两重循环遍历矩阵中所有的点。

本科在读,致力于Linux/C++方向的学习,同时涉及数据结构与算法,操作系统,数据库mysql等等 1265

Redis中什么是Big Key(大key)问题?如何解决Big Key问题?

通俗易懂的讲,Big Key就是某个key对应的value很大,占用的redis空间很大,本质上是大value问题。key往往是程序可以自行设置的,value往往不受程序控制,因此可能导致value很大。redis中这些Big Key对应的value值很大,在序列化/反序列化过程中花费的时间很大,因此当我们操作Big Key时,通常比较耗时,这就可能导致redis发生阻塞,从而降低redis性能。用几个实际的例子对大Key的特征进行描述:● 一个String类型的Key,它的值为5MB(数据过大); ● 一

weixiaohuai的博客 3万+

LeetCode第329题_矩阵中的最长递增路径

LeetCode 329题解析:矩阵中的最长递增路径问题,考察动态规划和记忆化DFS。文章提供两种解法:1) 记忆化DFS,通过深度优先搜索配合记忆数组避免重复计算;2) 动态规划,利用拓扑排序思想从出度为0的单元格开始更新。包含C#、Python、C++三种实现代码,时间复杂度均为O(mn)。适合算法基础扎实的程序员提升高级算法应用能力。

qq_40263592的博客 789

ORB-SLAM3单目摄像头

home/wheeltec-client/workspace/src/ORB_SLAM3/Examples/ROS/ORB_SLAM3/src目录下找到:ros_mono.cc文件,打开找到并修改成我们自己摄像头的订阅话题(usb_cam/imahe_raw)即相机设置文件,官方提供的是Asus.yaml,可以另复制命名一个文件My_camera.yaml,将里面的相机参数修改成自己标定过的参数。sudoaptinstalllibeieng3-dev#命令安装的版本是eigen。

疯狂的石头 742

工业机器人关节拆装流程_一文读懂模块化直驱电机及机器人关节模组

私信“干货”二字,即可领取138G伺服与机器人专属及电控资料!如果说芯片是智能行业最为重要的核心元件,那么电机几乎可以说是支撑了整个自动化和大工业生产以及现代社会最为重要的部件,全世界90%的能源是由电机发出来的,而70%是由电机消耗调的,电机可以说是整个人类社会能源循环的占有绝对地位的产品。普通伺服电机伺服电机其实已经算是电动机中比较高端的类型,特别是永磁同步类型的伺服电机,由于其启动快速,惯量...

weixin_32159771的博客 2155

算法题 矩阵中的最长递增路径

算法题 矩阵中的最长递增路径

红血哥 1202

TRON 主网节点搭建-2024最新详细版文档

TRON主网节点搭建以前,大概说明一下本次使用的服务器配置,本次从搭建部署到节点完成同步的时间大约为36小时。

J_Lee 4059

【C++】牛客 ——NC138 矩阵最长递增路径

你需要在矩阵中找到一条最长路径,使这条路径上的元素是递增的。:通过DFS遍历矩阵的每个单元格,从每个单元格开始搜索最长的递增路径。数据范围:1≤𝑛,𝑚≤10001≤n,m≤1000,0≤𝑚𝑎𝑡𝑟𝑖𝑥[𝑖][𝑗]≤10000≤matrix[i][j]≤1000。1. 对于每个单元格,你可以往上,下,左,右四个方向移动。例如:当输入为[[1,2,3],[4,5,6],[7,8,9]]时,对应的输出为5,方向数组包括四个方向:上、下、左、右。,用于存储从每个单元格开始的最长递增路径长度。

一岁就可帅的博客 1179

[深入研究4G/5G/6G专题-31]: 5G NR开机流程5.2 - UE Attach流程(Registeration Request/Accept/Complete)

目录前言:前置条件:第1章 5G NR UE attach流程(从UE log的视角看)1.0 NAS消息解析器1.1 消息交互流程1.2 UE Registeration Request消息(AMF->基站->UE)1.3 AMF Registeration Accept消息(AMF->基站->UE)1.4 UE Register complete消息(UE->基站->AMF)第2章 UE Register...............

文火冰糖(王文兵)的博客 7万+

Claude Code 100个真实案例 - 用AI搭建GIS地理信息系统(地图标注+热力图)

本案例展示如何使用 Claude Code 从零搭建一个完整的 GIS 地理信息系统。我们将使用 Python + Flask 作为后端,Leaflet.js 作为前端地图引擎,实现地图标注、热力图展示、区域绘制、坐标搜索等功能。全程通过与 Claude Code 对话完成,让你感受 AI 在地理信息领域的强大开发能力。

yueyeguzhuo的专栏 113

【数据结构算法】矩阵中的最长递增路径

题目 给定一个整数矩阵,找出最长递增路径的长度。 对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 解释: 最长递增路径为 [1, 2, 6, 9]。 示例 2: 输入: nums = [ [3,4,5],...

UESTC_201722的博客 1054

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

上一篇: 子集/回溯/变态递归
下一篇: 压缩字符串
周末的丢
博客等级 码龄7年 197粉丝 331原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值