算法49:动态规划专练(力扣1139题:最大正方形面积)

题目:

给你一个由若干 0 和 1 组成的二维网格 grid,请你找出边界全部由 1 组成的最大 正方形 子网格,并返回该子网格中的元素数量。如果不存在,则返回 0

示例 1:

输入:grid = [[1,1,1],[1,0,1],[1,1,1]]
输出:9

示例 2:

输入:grid = [[1,1,0,0]]
输出:1

这一题与算法48有点相似,但是它不能用单调栈解决这个问题。也不能用算法48的动态规划思想来解决。因为这一题是以全部为1的边组成的最大面积。也就是说中间是可以为0的。式例1就是最好的说明。

上一题用了单调栈、动态规划、暴力解。而这一题已经把前两种可能性已经排除掉了。只剩下暴力解了。那么,暴力解如何来解决这一题呢?

1. 正方形有4个顶点、4条边。只要验证每条边是否全部为1就可以了

2. 每增加一行一列,就验证新增的这些边是否全部为1就可以。如果验证不通过,没关系,继续往后验证。因为,当前新增的边并不一定就是最终的正方形边长,如果它只是最大正方形内部的一些元素而已,那我们根本不关注它是否为0.   

3. 需要注意的是,正方形的上方边和左方边出现了0, 那就不能继续去判断了。因为一点正方形的左上方顶点一旦确定,那上方边长和左方边长就已经确定了延长的方向了。但是,右侧的边和下方的边是要全部遍历完,才能最后确定的。

package code04.动态规划专项训练02;

/**
 * 力扣 1139  最大的以1为边界的正方形
 * https://leetcode.com/problems/largest-1-bordered-square/
 */
public class Largest1BorderedSquare_04_leetcode1139暴力解 {

    public int largest1BorderedSquare( int[][] matrix) {

        if (matrix == null || matrix.length == 0) {
            return 0;
        }

        int row = matrix.length;
        int col = matrix[0].length;
        //全局正方形最大边长
        int maxSide = 0;
        for (int i = 0; i < row ; i++) {
            //当前行的每一列都作为正方形的左上角,即起始点
            for (int j = 0; j < col; j++) {
                //当前格子是否为1,为1才可能成为正方形的起始点
                if (matrix[i][j] == 0) {
                    continue;
                }
                //整个二维数组中,重要有1出现,那正方形边长至少为1
                if (maxSide == 0) {
                    maxSide = 1;
                }

                //行边长,列边长,两者取小。因为是正方形
                int p = Math.min(row - i, col - j);
                //当前单元格 matrix[i][j] 开始的最大正方形边长
                int tempMaxSide = 1;
                boolean flag = true;

                //从i,j开始 到 i+index,j+index 范围内。全部都为1,才能验证通过
                //start为默认边长,默认边长为1. 因为matrix[i][j] == 1
                for (int count = 1; count < p; count++) {
                    //上方边长    matrix[i][j+count] == '0'
                    //左方边长    matrix[i + count][j] == '0'
                    if (matrix[i][j+count] == 0
                            || matrix[i + count][j] == 0) {
                        break;
                    }


                    int addCol = j + count;
                    int addRow = i + count;

                    //新增的行和列,不是全部范围
                    for (int m = 1; m <= count; m++) {
                           //下方边长 matrix[addRow][j + m]
                           //右方边长 matrix[i + m][addCol] == '0'
                           if (matrix[addRow][j + m] == 0
                                   || matrix[i + m][addCol] == 0) {
                               flag = false;
                           }
                    }

                    if (flag) {
                        tempMaxSide = Math.max(tempMaxSide, count + 1);
                    }
                    else {
                        //即使右边长、下方边长遇到0. 继续放行验证。
                        //因为当前边长有可能不是最终正方形的边界
                        flag = true;
                    }
                }

                /**
                 * 以当前单元格 matrix[i][j] 边长扩展完毕,
                 * tempMaxSide  以matrix[i][j] 为正方形左上顶点的最大边长为
                 * maxSide     整个区域之前最大正方形边长为
                 */
                maxSide = Math.max(maxSide, tempMaxSide);
            }
        }
        return maxSide * maxSide;
    }

    public static void main(String[] args) {
        Largest1BorderedSquare_04_leetcode1139暴力解 ss = new Largest1BorderedSquare_04_leetcode1139暴力解();

        //int[][] matrix = {{1,1,1}, {1,0,1},{1,1,1}};
        int[][] matrix = {{1,1,1}, {1,1,0},{1,1,1},{0,1,1},{1,1,1}};
        System.out.println(ss.largest1BorderedSquare(matrix));
    }
}

虽然只是暴力解,但是它只花费了5毫秒,胜率86%,已经相当的nice了。

那么,这一题是否有动态规划解法呢,答案是有。

下面来说一下动态规划的思路,以事例1为案例进行分析。

原始数组:

下标 0下标 1下标 2
下标 0111
下标 1101
下标 2111

由下往上,推算每个单元格距离底部的距离。遇到1就累加,遇到0就是0.

每个单元格下方1的数量:

下标 0下标 1下标 2
下标 0313
下标 1202
下标 2111

从右往左,推算出每个单元格右侧1的数量:

下标 0下标 1下标 2
下标 0321
下标 1101
下标 2321

1.  原始数组的0行0列为1,那么右侧有3个1, 下方也有3个1.  正方形的边长必须相等,因此两者取小。 也就是说,如果以原始数组0行0列为正方形的左顶点,那么这个正方形的最大边长肯定是小于等于3的。

2.  然后就是遍历了。最大长度为1,为2,为3,逐步验证。最终确定,最大正方形的的边长,到底是多少。

3. 在步骤1讨论的过程中,我们依旧确定了正方形上方边、左侧边的边长了。那么,下方的边、右侧的边也需要验证的。下方的边,可以通过左下方的顶点右侧1的数量来确定,必须大于步骤2讨论的边长。右侧的边可以通过正方形右上方的顶点来确定,右上方的顶点下方1的数量大于等于步骤2讨论的边长即可。

动态规划代码:

package code04.动态规划专项训练02;

/**
 * 力扣 1139  最大的以1为边界的正方形
 * https://leetcode.com/problems/largest-1-bordered-square/
 */
public class Largest1BorderedSquare_04_leetcode1139动态规划 {

    //统计每个单元格,右侧连续有几个1;下方连续有几个1;
    public void preHandle(int[][] matrix, int[][] rightArr, int[][] bottomArr)
    {
        int rowLength = matrix.length;
        int colLength = matrix[0].length;

        bottomArr[rowLength-1][colLength-1] = matrix[rowLength-1][colLength-1];
        rightArr[rowLength-1][colLength-1] = matrix[rowLength-1][colLength-1];

        //最后一行.
        for  (int i = colLength - 2; i >= 0; i--) {
            //距离底部有几个1
            bottomArr[rowLength - 1][i] = matrix[rowLength - 1][i];
            //距离右侧有几个1
            rightArr[rowLength - 1][i] = matrix[rowLength - 1][i] == 0 ? 0 : matrix[rowLength - 1][i] + rightArr[rowLength - 1][i + 1];
        }

        //最后一列
        for  (int i = matrix.length - 2; i >= 0; i--) {
            //距离底部有几个1
            bottomArr[i][colLength - 1] = matrix[i][colLength - 1] == 0 ? 0 : matrix[i][colLength - 1] + bottomArr[i + 1][colLength - 1];
            //距离右侧有几个1
            rightArr[i][colLength - 1] = matrix[i][colLength - 1];
        }


        for  (int i = matrix.length -2; i >= 0; i--) {
            for (int j = matrix[0].length - 2; j >= 0; j--) {
                if (matrix[i][j] == 1) {
                    bottomArr[i][j] = matrix[i][j] + bottomArr[i+1][j];
                    rightArr[i][j] = matrix[i][j] + rightArr[i][j + 1];
                }
            }
        }
    }

    public int largest1BorderedSquare(int[][] matrix) {

        if (matrix == null || matrix.length == 0) {
            return 0;
        }

        int row = matrix.length;
        int col = matrix[0].length;

        int[][] rightArr = new int[row][col];
        int[][] bottomArr = new int[row][col];

        //预处理数组
        preHandle(matrix, rightArr, bottomArr);

        //全局正方形最大边长
        int maxSide = 0;
        for (int i = 0; i < row ; i++) {
            //当前行的每一列都作为正方形的左上角,即起始点
            for (int j = 0; j < col; j++) {
                //当前格子是否为1,为1才可能成为正方形的起始点
                if (matrix[i][j] == 0) {
                    continue;
                }
                //整个二维数组中,重要有1出现,那正方形边长至少为1
                if (maxSide == 0) {
                    maxSide = 1;
                }

                //行边长,列边长,两者取小。因为是正方形
                int sideLength = Math.min(rightArr[i][j], bottomArr[i][j]);
                boolean flag = true;
                for (int m = 1; m < sideLength; m++) {
                    //正方形的右上顶点往下数,即右边边长
                    int rightLength = bottomArr[i][j + m];
                    //正方形的底部边长
                    int bottomLength = rightArr[i + m][j];

                    //当前边长
                    int length = m + 1;
                    if (rightLength >= length && bottomLength >= length) {
                        maxSide = Math.max(maxSide, length);
                    }
                }
            }
        }
        return maxSide * maxSide;
    }

    public static void main(String[] args) {
        Largest1BorderedSquare_04_leetcode1139动态规划 ss = new Largest1BorderedSquare_04_leetcode1139动态规划();

        //int[][] matrix = {{1,1,1}, {1,0,1},{1,1,1}};
        int[][] matrix = {{1,1,1}, {1,1,0},{1,1,1},{0,1,1},{1,1,1}};
        System.out.println(ss.largest1BorderedSquare(matrix));
    }
}

7毫秒,50%的胜率,也还可以了。

虽然这一题也有动态规划,但是,它是基于暴力解逐步演化过来的。暴力解法时间复杂度为 O(N^4)。我们基于暴力解在讨论边长的过程中还需要一次遍历,通过数组预处理的的方式提前进行了统计,简化了验证新增列的验证。因此,动态规划的时间复杂度为 O(N^3) + O(N^2).  实际就是O(N^3)。

这一道题,动态规划的性能比暴力解的还要低一些,虽然动态规划的时间复杂度为O(N^3),而暴力解的时间复杂度为O(N^4)。

动态规划的时间复杂度为O(N^3),但是,它还有一个O(N^2)预处理数组的过程。因为力扣数据量有限,动态规划时间复杂度虽然降低了一阶,但是直观看起来还是没有暴力解高。

随着数据量的增大,动态规划还是要优于暴力解的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【如何成为一名好的系统架构设计师】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱1.如何成为一名好的系统架构设计师1.1 如何衡量一名优秀架构设计师1) 作为技术领导者2) 作为开发人员3) 聚焦系统4) 具备企业家思维5) 权衡策略思维与战术思维6) 良好的沟通 1.2 从工程师到系统架构设计师的…

sql server使用逗号,分隔保存多个id的一些查询保存

方案一&#xff0c;前后不附加逗号&#xff1a; 方案二&#xff0c;前后附加逗号&#xff1a; 其他保存方案&#xff1a; &#xff08;这里是我做一个程序的商家日期规则搞得&#xff0c;后面再补具体操作&#xff09;&#xff1a; 1,2,3 | 1,2,3 | 1,2,3; 1,2,3 &#xff1…

如何恢复未保存的 Excel 文件

本周我们将 Office 恢复系列扩展到 Excel 恢复&#xff0c;并提出了最常见的问题&#xff1a;如何恢复 Excel 文件&#xff1f; 与 Office Word 不同&#xff0c;Excel 完全是关于表格和计算的。在处理Excel文件时&#xff0c;您可能会遇到更多问题。与往常一样&#xff0c;我们…

STM32CubeMX学习笔记15---CAN总线

1、CAN简介 CAN总线网络的结构有闭环和开环两种形式 闭环结构的CAN总线网络&#xff0c;总线两端各连接一个1202的电阻。这种CAN总线网络由ISO11898标准定义&#xff0c;是高速、短距离的CAN网络&#xff0c;通信速率为125kbit/s到1Mbit/s。在1Mbit/s通信速率时&#x…

【嵌入式——QT】Model/View

【嵌入式——QT】Model/View 基本原理数据模型视图组件代理Model/View结构的一些概念QFileSystemModelQStringListModelQStandardItemModel自定义代理 基本原理 GUI应用程序的一个很重要的功能是由用户在界面上编辑和修改数据&#xff0c;典型的如数据库应用程序&#xff0c;数…

wsl 安装 ubuntu

文章目录 打开Windows PowerShell查看可安装的ubuntu安装相对应的ubuntu将用户添加到sudoers文件中&#xff0c;并赋予了该用户sudo权限。 打开Windows PowerShell 以管理员的身份运行 查看可安装的ubuntu wsl.exe --list --online安装相对应的ubuntu wsl --install 版本…

计算机网络面经-HTTPS加密过程

前言 在上篇文章HTTPS详解一中&#xff0c;我已经为大家介绍了 HTTPS 的详细原理和通信流程&#xff0c;但总感觉少了点什么&#xff0c;应该是少了对安全层的针对性介绍&#xff0c;那么这篇文章就算是对HTTPS 详解一的补充吧。还记得这张图吧。 HTTPS 和 HTTP的区别 显然&am…

安装zabbix

部署Zabbix监控平台 部署一台Zabbix监控服务器&#xff0c;一台被监控主机&#xff0c;为进一步执行具体的监控任务做准备&#xff1a; 安装LNMP环境源码安装Zabbix安装监控端主机&#xff0c;修改基本配置初始化Zabbix监控Web页面修改PHP配置文件&#xff0c;满足Zabbix需求…

2024 GoLand激活,分享几个GoLand激活的方案

文章目录 GoLand公司简介我这边使用GoLand的理由GoLand 最新变化GoLand 2023.3 最新变化AI Assistant 正式版GoLand 中的 AI Assistant&#xff1a;_Rename_&#xff08;重命名&#xff09;GoLand 中的 AI Assistant&#xff1a;_Write documentation_&#xff08;编写文档&…

VScode+Zotero+Latex文献引用联动

一、VScodeLatex联动 1、VScode的安装 2、texlive.iso安装 可以参考以下&#xff0c;也可以忽略所有直接一步一步默认安装 https://zhuanlan.zhihu.com/p/442308176 3、Vscode的插件安装&#xff1a;【latex workshop】 4、打开设置&#xff0c;搜索json&#xff0c;然后点击…

最新基于R语言lavaan结构方程模型(SEM)技术应用

结构方程模型&#xff08;Sructural Equation Modeling&#xff0c;SEM&#xff09;是分析系统内变量间的相互关系的利器&#xff0c;可通过图形化方式清晰展示系统中多变量因果关系网&#xff0c;具有强大的数据分析功能和广泛的适用性&#xff0c;是近年来生态、进化、环境、…

JVM运行时数据区——对象的实例化内存布局与访问定位

文章目录 1、对象的实例化1.1、创建对象的方式1.2、创建对象的步骤 2、对象的内存布局3、对象的访问定位3.1、对象访问的定位方式3.2、使用句柄访问3.3、使用指针访问 4、小结 平时大家经常使用new关键字来创建对象&#xff0c;那么我们创建对象的时候&#xff0c;怎么去和运行…

css 用flex做成田字型

哈喽&#xff0c;各位小伙伴&#xff01;今天给大家来css控制div完成田字型样式&#xff0c;来&#xff0c;看看下面的效果图&#xff1a; 一看就知道你们想要代码了&#xff0c;不急。代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head>&…

Qt/C++音视频开发68-检查是否含有B帧/转码推流/拉流显示/监控拉流推流/海康大华宇视监控

一、前言 为什么需要判断视频文件是否含有B帧&#xff0c;这个在推流的时候很容易遇到这个问题&#xff0c;一般来说&#xff0c;没有B帧的视频文件&#xff0c;解码后的数据帧pts和dts都是顺序递增的&#xff0c;而有B帧的则未必&#xff0c;可能有些需要先解码后面显示&…

uniapp+node.js前后端做帖子模块:发布帖子评论(社区管理平台的小程序)

目录 0前提1.一些准备1.1表帖子表 post帖子评论表 postComment 1.2总体思路 2.前端3.后端4.验证结果 &#x1f44d; 点赞&#xff0c;你的认可是我创作的动力&#xff01; ⭐️ 收藏&#xff0c;你的青睐是我努力的方向&#xff01; ✏️ 评论&#xff0c;你的意见是我进步的…

CVPR 2022 Oral | Bailando: 基于编舞记忆和Actor-Critic GPT的3D舞蹈生成

目录 测试结果&#xff1a; 02 提出的方法 测试结果&#xff1a; 预测有3个步骤&#xff0c;速度比较慢 02 提出的方法 1. 针对舞蹈序列的VQ-VAE和编舞记忆 与之前的方法不同&#xff0c;我们不学习从音频特征到 3D 关键点序列的连续域的直接映射。相反&#xff0c;我们先让…

Springboot 的几种配置文件形式

方式一&#xff1a;多个yml文件 步骤1&#xff1a;创建多个配置文件 application.yml #主配置文件 application-dev.yml #开发环境的配置 application-prod.yml #生产环境的配置 application-test.yml #测试环境的配置步骤2&#xff1a;applicaiton.yml中指定配置 在a…

算法Day05_707.设计链表

推荐阅读 算法day01_ 27. 移除元素、977.有序数组的平方 算法day02_209.长度最小的子数组 算法day03_ 59.螺旋矩阵II 算法Day04_203.移除链表元素 目录 推荐阅读707.设计链表题目思路解法单链表解法双链表解法 707.设计链表 题目 你可以选择使用单链表或者双链表&#xff0c;设…

桶装水系统订水送水软件有哪些实用功能?

桶装水配送系统送水订水小程序预约水票开发定制桶装水管理软件特色; 1、订水软件界面简洁明了&#xff0c;操作简单易上手 2、桶装水管理软件正式版软件的功能全面&#xff0c;涉及到了桶装水后台管理的全部流程 3、财务报表可以自动计算出桶装水销售的详细数据 4、仓库管理、仓…

Android14音频进阶:AudioTrack与AudioFlinger创建数据通道(五十八)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…