Leetcode 2713. 矩阵中严格递增的单元格数(DFS DP)

news2025/1/9 5:57:25

Leetcode 2713. 矩阵中严格递增的单元格数

DFS

容易想到,枚举每个点作为起点,向同行同列的可跳跃点dfs,维护全局变量记录可达的最远距离

超时,通过样例193 / 566

class Solution {
    int res = 0;
    
    public void dfs(int[][] mat, int x, int y, int step){
        step ++;
        res = Math.max(res, step);
        int n = mat.length;
        int m = mat[0].length;
        int num = mat[x][y];
        for(int i = 0; i < n; i ++){
            if(mat[i][y] > num){
                dfs(mat, i, y, step);
            }
        }
        for(int j = 0; j < m; j ++){
            if(mat[x][j] > num){
                dfs(mat, x, j, step);
            }
        }
        return;
    }

    public int maxIncreasingCells(int[][] mat) {
        int n = mat.length;
        int m = mat[0].length;
        for(int i = 0 ; i < n; i ++){
            for(int j = 0 ; j < m; j ++){
                dfs(mat, i, j, 0);
            }
        }
        return res;
    }
}

DFS+记忆化搜索

在DFS中注意到,存在很多重复计算,无论以哪个点作为起点,当跳跃到(x, y)点后,后续的最远距离是固定的,若在其他的起点中已经计算过这个数值,则记录下来直接取用

使用mem[ i ][ j ]记录坐标(i, j)位置所能跳跃的最大距离,在进入DFS后,若这个点已经计算过,即mem[ i ][ j ]非0,则直接取用。若没有计算过,则进行DFS,并在DFS结束后更新mem[ i ][ j ]

超时,通过样例558 / 566

class Solution {
    int mem[][];
    // dfs函数返回(x,y)坐标可达最远距离,包含自身,最少为1
    public int dfs(int[][] mat, int x, int y){
        if(mem[x][y] != 0){
            return mem[x][y];
        }
        int n = mat.length;
        int m = mat[0].length;
        int res = 1;
        int num = mat[x][y];
        for(int i = 0; i < n; i ++){
            if(mat[i][y] > num){
                int ans = dfs(mat, i, y);
                res = Math.max(res, ans + 1);
            }
        }
        for(int j = 0; j < m; j ++){
            if(mat[x][j] > num){
                int ans = dfs(mat, x, j);
                res = Math.max(res, ans + 1);
            }
        }
        mem[x][y] = res;
        return res;
    }

    public int maxIncreasingCells(int[][] mat) {
        int n = mat.length;
        int m = mat[0].length;
        mem = new int [n][m];
        int res = 0;
        for(int i = 0 ; i < n; i ++){
            for(int j = 0 ; j < m; j ++){
                res = Math.max(res, dfs(mat, i, j));
            }
        }
        return res;
    }
}

DP

经过优化无法使用DFS通过所有样例,重新分析问题

对于点(x, y)来说,以其作为起点,该点所能到达的最远距离,取决于该行和该列中其他点所能到达的最远距离,即为Max(行可达最远,列可达最远)+1

但移动规则为只能向更大的点进行移动,若每次移动前对行列所有元素都进行检查同样会造成时间浪费,因此将n*m个位置根据值进行降序排序,从大到小进行处理

这样带来的好处是,无需考虑跳跃规则问题,当处理一个元素时,已经记录的行可达最远的点是一定大于它的,一定可以向其移动

另一方面,只需求出移动的最远距离,因此无需记录一次移动使用哪个位置转移过来的,只需要知道此次移动的距离是多少就可以,即第 i 行的行可达最远只需要O(1)的空间,来实时维护此时该行已经处理过的最远距离,列同理

由此构思所需要的空间
dp[ i ][ j ] 记录(i, j)位置为起点,所能移动的最远距离
rowMax[ i ] 记录第 i 行此时已经存在的最远距离,即该行中存在一个点,该点为起点所能移动的距离最远为rowMax[ i ],初始化为0
colMax[ j ] 记录第 j 列此时已经存在的最远距离,即该列中存在一个点,该点为起点所能移动的距离最远为colMax[ i ],初始化为0
则dp[ i ][ j ] = Max{ rowMax[ i ], colMax[ j ] } + 1
计算后更新rowMax[ i ] 和 colMax[ j ]

在这里插入图片描述

起点7:dp = max(0, 0) + 1 = 1,更新rowMax[ 1 ] = 1,更新colMax[ 2 ] = 1
起点6:dp = max(0, 1) + 1 = 2,更新rowMax[ 0 ] = 2,更新colMax[ 2 ] = 2
起点5:dp = max(1, 0) + 1 = 2,更新rowMax[ 1 ] = 2,更新colMax[ 1 ] = 2
起点3:dp = max(2, 0) + 1 = 3,更新rowMax[ 0 ] = 3,更新colMax[ 0 ] = 3
起点1:dp = max(3, 2) + 1 = 4,更新rowMax[ 0 ] = 4,更新colMax[ 1 ] = 4
起点-9:dp = max(2, 3) + 1 = 4,更新rowMax[ 1 ] = 4,更新colMax[ 0 ] = 4

在这里插入图片描述
另外注意,存在相同元素,此时计算dp时不应更新rowMax,违背了rowMax的定义“该行内存在一点,以其为起点所答的最远距离为rowMax[ i ],dp[ i ] [ j ] = rowMax + 1”,因为对于同样的元素来说,他们之间是不可达的,因此对于相同元素来说,此时更新rowMax会导致从值k移动到值k 的情况发生

因此将相同元素作为一批同时处理,其间使用rowMax计算dp,而定义一个新数组rowTemp来临时记录rowMax应有的变化,当这一批值相同的元素dp计算结束后,再将rowTemp赋值给rowMax以用于后续的计算,colMax同理

class Solution {
	// Pair类记录一个点的值和坐标,用于排序
    public class Pair{
        private int val;
        private int x;
        private int y;

        public Pair(int val, int x, int y) {
            this.val = val;
            this.x = x;
            this.y = y;
        }
    }

    public int maxIncreasingCells(int[][] mat) {
        int n = mat.length;
        int m = mat[0].length;
        Pair pairs[] = new Pair[n * m];
        int index = 0;
        for(int i = 0 ; i < n; i ++){
            for(int j = 0; j < m; j ++){
                pairs[index++] = new Pair(mat[i][j], i, j);
            }
        }
		// 降序排序所有点
        Arrays.sort(pairs, (p1, p2) -> Integer.compare(p2.val, p1.val));
        int rowMax[] = new int [n];
        int colMax[] = new int [m];
        int dp[][] = new int [n][m];
        int res = 0;
        for(int i = 0 ; i < n*m; i ++){
            int val = pairs[i].val;
            int x = pairs[i].x;
            int y = pairs[i].y;
            // 相同元素情况
            if(i+1 < n*m && pairs[i+1].val == val){
                int l = i;
                int r = i + 1;
                // 确定范围
                while(r < n*m && pairs[r].val == val)
                    r ++;
                // rowMax副本
                int rowTemp[] = rowMax.clone();
                int colTemp[] = colMax.clone();
                // 统一处理计算dp
                for(int k = l; k < r; k ++){
                    int kx = pairs[k].x;
                    int ky = pairs[k].y;
                    int kval = pairs[k].val;
                    dp[kx][ky] = Math.max(rowMax[kx], colMax[ky]) + 1;
                    res = Math.max(res, dp[kx][ky]);

                    // 将该行最远距离的变化暂存在rowTemp,保持rowMax不变
                    rowTemp[kx] = Math.max(rowTemp[kx], dp[kx][ky]);
                    colTemp[ky] = Math.max(colTemp[ky], dp[kx][ky]);
                }
                // dp计算后将rowMax值更新
                for(int j = 0; j < n; j ++){
                    rowMax[j] = rowTemp[j];
                }
                for(int j = 0; j < m; j ++){
                    colMax[j] = colTemp[j];
                }
                i = r - 1;
            }
            // 不同元素情况直接计算
            else{
                dp[x][y] = Math.max(rowMax[x], colMax[y]) + 1;
                res = Math.max(res, dp[x][y]);
                rowMax[x] = dp[x][y];
                colMax[y] = dp[x][y];
            }
        }
        return res;
    }
}

相同元素处理部分写的比较繁琐了

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

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

相关文章

网络编程之XDP、TC和IO_URING以及DPDK

一、网络编程常见的技术 在前面已经分析过了XDP、TC和eBPF。也基本把三者间的关系理清了&#xff0c;但现在又有一个疑惑涌了上来。在前面提到过的IO_URING和DPDK与这些技术有什么关系呢&#xff1f;其实只要认真的看过分析文章可能大家心里都已经基本清楚了。 正如在前面不断…

视频格式怎么转换?9 个免费视频转换工具

前 9 款免费视频转换器有哪些&#xff1f;在此视频转换器评论中&#xff0c;我们收集了一些有用的提示并列出了顶级免费视频转换器软件&#xff0c;还找出了适合所有级别&#xff08;从初学者到专家&#xff09;的最佳免费视频转换器。 1. Geekersoft免费在线视频转换 最好的免…

【HTTPS云证书部署】SpingBoot部署证书

这里以华为云证书为例。 1. 下载证书 2. 解压 3. 选择.top_Tomcat复制到SpringBoot的Resource/source下 4. 在.properties文件中进行配置 修改key-store和key-store-password

vue3源码(五)ref、toRef、toRefs、proxyRefs

1.ref 功能 ref与reactive功能类似,都是将数据变为响应式&#xff0c;ref通常用来定义基本类型数据&#xff0c;如字符串、数字、布尔值等。而reactive用来定义对象&#xff08;或数组&#xff09;类型数据。虽然ref也可以用来定义对象或数组类型的数据&#xff0c;但内部会通…

Rxjava2最全面的解析

说到区别&#xff0c;可能有的小伙伴会问&#xff0c;我没看过rxjava1。可以直接看rxjava2么。个人觉得不必要&#xff0c;因为 rxjava2.x 是按照 Reactive-Streams specification 规范完全的重写的&#xff0c;完全独立于 rxjava1.x 而存在&#xff0c;它改变了以往 rxjava1的…

Springboot 项目启动时扫描所有枚举并存入缓存(redis)

为什么这么做? 为了springboot 注解属性转换字典方便一点(使用缓存的方式在Springboot 启动时获取字典数据) 在启动时会扫描com.vehicle.manager.core.enumerate包下的所有枚举(包括类中的内部枚举),并取出对应属性以json的方式存入redis 目录结构如下: RedisUtil可以在Red…

已解决javax.management.BadStringOperationException异常的正确解决方法,亲测有效!!!

已解决javax.management.BadStringOperationException异常的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 目录 问题分析 出现问题的场景 报错原因 解决思路 解决方法 分析错误日志 检查字符串值合法性 确认字符串格式 优化代码逻辑 增加…

【数据结构与算法】图论 详解

何为完全图、稀疏图、稠密图。 完全图&#xff1a;完全图是一种简单的无向图&#xff0c;其中每对不同的顶点之间都恰好有一条边。对于有n个顶点的完全图&#xff0c;它包含n(n-1)/2条边。在有向图中&#xff0c;如果任意两个顶点之间都存在方向相反的两条边&#xff0c;包含n(…

汇编快速入门

一.基础知识 1.数据类型 DB&#xff08;Define Byte&#xff0c;字节类型 占位8位bit 1字节&#xff09; 范围&#xff1a;DB可以用来定义&#xff08;无符号、有符号&#xff09;整数&#xff08;包含二、十、十六进制&#xff09;和字符 语法&#xff1a;a DB 数据个数…

C++基础知识——引用

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 博主主页&#xff1a;Yan. yan.                        …

热电阻温度计的测量电路

热电阻温度计的测量电路&#xff0c;为达到高精度测量&#xff0c;通常会采用电桥测量原理&#xff0c;并结合适当的热电阻类型和连接方式。 惠斯通电桥是用于测量一组电阻式元件阻值变化的电路。该电路具有两个并联电阻支路&#xff0c;充当激励电压 VEXCITATION 的分压器。每…

C++ 教程 - 04 类的使用

文章目录 类的定义类定义案例构造函数 类的定义 C 在 C 语言的基础上增加面向对象编程&#xff0c;类是用于指定对象的形式&#xff0c;是一种用户自定义的数据类型&#xff0c;封装了数据和函数。类可以被看作是一种模板&#xff0c;可以用来创建具有相同属性和行为的多个对象…

LLM技术全景图:技术人必备的技术指南,一张图带你掌握从基础设施到AI应用的全面梳理

LLM技术全景图&#xff1a;技术人必备的技术指南&#xff0c;一张图带你掌握从基础设施到AI应用的全面梳理 LLM 技术图谱&#xff08;LLM Tech Map&#xff09;是将 LLM 相关技术进行系统化和图形化的呈现&#xff0c;此图谱主要特点是“专注于技术人视角”&#xff0c;不求从…

【吊打面试官系列-Mysql面试题】视图有哪些优点?

大家好&#xff0c;我是锋哥。今天分享关于 【视图有哪些优点&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 视图有哪些优点&#xff1f; 答&#xff1a; (1) 视图能够简化用户的操作&#xff1b; (2) 视图使用户能以多种角度看待同一数据&#xff1b; (3) 视…

深入学习Java1213新特性

一、关于Java生态圈 二、Java老矣&#xff0c;尚能饭否&#xff1f; 三、JDK各版本主要特性 四、JDK与IDE的下载与安装 五、Java12新特性 1.switch表达式&#xff08;预览&#xff09; 2.Shenandoah GC:低停顿时间的GC&#xff08;预览&#xff09; 3.JVM常量API 4.微基准测试套…

Vue76-路由对浏览器历史记录的影响

一、push模式 默认是push 二、replace模式 替换当前记录&#xff01; &#xff08;当前指针指向的那一条记录&#xff09; 三、小结

Tobii Pro Lab 1.232是全球领先的眼动追踪研究实验软件

Tobii Pro Lab是全球领先的眼动追踪研究实验软件。软件功能强大且拥有友好的用户界面&#xff0c;使眼动追踪研究变得更加简单、高效。该软件提供了很高的灵活性&#xff0c;可运行高级实验&#xff0c;深入了解注意力和认知过程。 获取软件安装包以及永久授权联系邮箱:289535…

QT事件处理系统之二:窗口部件的事件拦截,以及事件的传递顺序

1、案例说明 在父窗口中为selfLineEdit窗口安装事件过滤器,这样我们可以在父窗口中首先拦截来自于selfLineEdit本身产生的事件,并且决定该事件最终是否继续传递到selfLineEdit窗口本身。 2、关键代码 selfLineEdit.cpp #include "selfLineEdit.h" #include &l…

getPhysicalNumberOfCells获取列数不是合并前实际列数

问题就是:有的导入复杂表头被合并的单元格有默认空字符串&#xff0c;有的直接不存在这个单元格 实际我需要下面这种情况 断点可以看到这个导入第一行合并了&#xff0c;被合并单元格还有默认的空字符串 解决办法就是在合并单元格里面判断&#xff0c;不是第一行第一列都设置…

软件培训方案(Word原件)

1. 培训目的 2. 培训方式 3. 培训内容 4. 培训讲师 5. 培训教材 6. 培训质量保证 软件全套资料&#xff1a;本文末个人名片直接获取或者进主页。