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

题目:

在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

示例 1:

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4

示例 2:

输入:matrix = [["0","1"],["1","0"]]
输出:1

示例 3:

输入:matrix = [["0"]]
输出:0

我之前写过一篇博客,是利用单调栈计算最大矩形面积的。感兴趣的看一下:算法37:最大矩形面积(力扣84、85题)---单调栈-CSDN博客

而本道题目求的是最大正方形面积,因此,利用单调栈完全可以解决。思路和求最大矩形面积是一样的,下面直接贴出单调栈的代码:

单调栈解法:

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

/**
 *  力扣 221题  最大正方形面积
 *  https://leetcode.cn/problems/maximal-square/?envType=study-plan-v2&envId=dynamic-programming
 */
public class MaximalSquare_03_leetcode221单调栈 {

    public int maximalSquare(char[][] matrix) {

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

        int[] dp = new int[matrix[0].length];
        int res = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                //数组压缩
                int cur = matrix[i][j] == '0' ? 0 : Integer.parseInt(String.valueOf(matrix[i][j]));
                dp[j] = cur == 0 ? 0 : dp[j] + cur;
            }
            res = Math.max(res, getMaxValue(dp));
        }

        return res;
    }

    public int getMaxValue (int[] arr) {

        int result = 0;
        int size = arr.length;
        //自定义栈结构
        int[] stack = new int[arr.length];
        int stackSize = 0;

        for (int i = 0; i < arr.length; i++) {

            while (stackSize != 0 && arr[stack[stackSize-1]] >= arr[i]) {
                //当前弹出数的下标
                int curIndex = stack[--stackSize];
                //左边第一个比curIndex数组对应数小的下标
                int leftIndex = stackSize == 0 ? -1 : stack[stackSize -1];
                //右边第一个比curIndex数组对应数小的下标
                int rightIndex = i;

                int width = Math.min(arr[curIndex], rightIndex - leftIndex - 1);

                result = Math.max(result, width * width);
            }

            stack[stackSize++] = i;
        }

        //如果还有剩余
        while (stackSize != 0) {
            //当前弹出数的下标
            int curIndex = stack[--stackSize];

            //左边第一个比curIndex数组对应数小的下标
            int leftIndex = stackSize == 0 ? -1 : stack[stackSize -1];

            int width = Math.min(arr[curIndex], size - leftIndex - 1);

            result = Math.max(result, width * width);
        }

        return result;
    }

    public static void main(String[] args) {
        MaximalSquare_03_leetcode221单调栈 ss = new MaximalSquare_03_leetcode221单调栈();
        char[][] matrix = {{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}};
        System.out.println(ss.maximalSquare(matrix));
    }
}

 

胜率虽然不高,但是只花费了 12毫秒, 还算可以的了。

本章是动态规划专练,因此,不得不说一下动态规划的思路:

1. 我们知道,正方是有4个顶点,并且每条边的边长都是相等的。

2. 本道题规定,如果每个单元格的值为1,那么它最小也可以看成是变成为1的正方形。如果为0,当前单元格就不能算作正方形。

3. 如果只有4个顶点,想要组成最大边长为2的正方形,那么这4个顶点必定全部都为1.  如果以最后一个顶点为正方形的结束点,那么依赖的其他三个点,必定全部不为0.

 

X1X3
X2

1 + Math.min(X1, Math.min(X2, X3))

(依赖左、上、左上3个顶点的最小值)

也就是说,目前看到的这些节点,以当前单元格

结束,那么它的最大边长就是1 + Math.min(X1, Math.min(X2, X3))

为什么要依赖最小值?

因为,X1、X2、X3任何一点为0, 那么以当前单元格为正方形的结束节点,那么它的最大正方形边长就为1. 当然,当前单元格的值必须不为0.

4. 第一行,正方形的边长最大为1; 第一列,最大正方形的边长也为1. 因为正方形边长是需要相等的。

我们根据事例1给的数据进行推导:

第一步:

下标01234
下标010100
11

原始数组为0,

无法参与到正

方形的结束点.

直接取0

前一列为0,

上一列为1.取小。

当前列为1. 

因此, 1 + 0 = 1

1 + 0 = 11 + 0 = 1
21
31

第二步:根据上方规则,类推

下标01234
下标010100
11

0

1

11
211 + 0 = 11 + 1 = 21+ 1 = 21+ 1 = 2
31

第三步:根据上方规则,类推

下标01234
下标010100
11

0

1

11
211222
31001+0 = 10

 目测就知道,最大正方形的边长为2. 面积为 4;

动态规划代码:

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

/**
 * 力扣 221题  最大正方形面积
 * https://leetcode.cn/problems/maximal-square/?envType=study-plan-v2&envId=dynamic-programming
 */
public class MaximalSquare_03_leetcode221动态规划 {

    public int maximalSquare(char[][] matrix) {

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

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

        int[][] dp = new int[row][col];
        int maxSide = 0;
        //第一行
        for (int i = 0; i < col; i++) {
            dp[0][i] = matrix[0][i] == '0' ? 0 : 1;

            if (maxSide == 0 && dp[0][i] > 0) {
                maxSide = 1;
            }
        }

        //第一列
        for (int i = 0; i < row; i++) {
            dp[i][0] = matrix[i][0] == '0' ? 0 : 1;

            if (maxSide == 0 && dp[i][0] > 0) {
                maxSide = 1;
            }
        }

        for (int index1 = 1; index1 < row; index1++) {
            for (int index2 = 1; index2 < col; index2++) {
                if (matrix[index1][index2] == '1') {
                    //左上顶点
                    int p1 = dp[index1 - 1][index2 - 1];
                    //左顶点
                    int p2 = dp[index1][index2 - 1];
                    //上顶点
                    int p3 = dp[index1 - 1][index2];

                    dp[index1][index2] = Math.min(p3, Math.min(p1, p2)) + 1;
                } else {
                    dp[index1][index2] = 0;
                }

                maxSide = Math.max(maxSide, dp[index1][index2]);
            }
        }
        return maxSide * maxSide;
    }

    public static void main(String[] args) {
        MaximalSquare_03_leetcode221动态规划 ss = new MaximalSquare_03_leetcode221动态规划();
        //char[][] matrix = {{'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'}};
        //char[][] matrix = {{'0', '1'}, {'1', '0'}};
        char[][] matrix = {{'0', '1'}};
        System.out.println(ss.maximalSquare(matrix));
    }
}

它的确比单调栈更快,效率更高。而且,它的思路更加的简单。

但是,它只能解决正方形的问题,如果是矩形,当前思路都无法解决。因此,他有一定的局限性。

下一道题,同样也是算最大正方形面积的题目。但是,单调栈、本章的动态规划推导思路完全不适用,只能用暴力方法去解决。因此,本道题的暴力方法,也必须得介绍一下。

暴力解法:

1. 遍历全部单元格,找出每个单元格作为正方形顶点的最大边长。

2. 在步骤1找的过程中,是范围内的验证。

暴力解法思路相对简单,只是代码比较复杂

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

/**
 * 力扣 221题  最大正方形面积
 * https://leetcode.cn/problems/maximal-square/?envType=study-plan-v2&envId=dynamic-programming
 * <p>
 * 基本思路:
 * 1. 当前列为正方形起点。
 * 从左往右,推算出最大边长; 从上往下推算出最大边长; 因为是正方形,因此之前两个边长取小
 * 2. 对这个范围内的所有格子进行为1验证
 */
public class MaximalSquare_03_leetcode221暴力解 {

    public int maximalSquare(char[][] matrix) {

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

        int row = matrix.length;
        int col = matrix[0].length;
        //最大边长
        int max = 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 (max == 0) {
                    max = 1;
                }
                //行边长,列边长,两者取小。因为是正方形
                int p = Math.min(row - i, col - j);
                //从i,j开始 到 i+index,j+index 范围内。全部都为1,才能验证通过
                //start为默认边长,默认边长为1. 因为matrix[i][j] == 1
                for (int count = 1; count < p; count++) {
                    //斜线
                    if (matrix[i + count][j+count] == '0') {
                        break;
                    }

                    boolean flag = true;
                    // 如果上边行、左边列延伸都不为0;那就继续校验正方形
                    // 内部是否全为1
                    for (int m = 0; m < count; m++) {
                        if (matrix[i + count][j + m] == '0' || matrix[i + m][j + count] == '0') {
                            flag = false;
                            break;
                        }
                    }

                    if (flag) {
                        //默认边长为1, 即当前正方形做顶点为1
                        //count是新增的长度。 count+1 代表正常放边长
                        max = Math.max(max, count + 1);
                    }
                    else {
                        break;
                    }
                }

            }
        }
        return max * max;
    }

    public static void main(String[] args) {
        MaximalSquare_03_leetcode221暴力解 ss = new MaximalSquare_03_leetcode221暴力解();
        //char[][] matrix = {{'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'}};
        char[][] matrix = {{'0', '1'}, {'1', '0'}};
        System.out.println(ss.maximalSquare(matrix));
    }
}

因此,本道题使用单调栈、动态规划性能更高。暴力解法,性能低。因为下一道题,只能基于暴力解法完成并优化,因此,不得不提前在此提一下暴力解法。

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

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

相关文章

MySQL下实现纯SQL语句的递归查询

需求 有一个部门表&#xff0c;部门表中有一个字段用于定义它的父部门&#xff1b; 在实际业务中有一个『部门中心』的业务&#xff1b; 比如采购单&#xff0c;我们需要显示本部门及子部门的采购单显示出来。 结构 数据如下&#xff1a; 实现方式如下&#xff1a; WITH RECUR…

打造智慧足球社区:Java+SpringBoot实战

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

【vue3之组合式API及其新特性】

组合式API及其新特性 一、setup1.写法2.如何访问3.语法糖4.同步返回对象 二、reactive()和ref()1.reactive()2.ref() 三、computed四、watch函数1.侦听单个数据2.侦听多个数据3. immediate4. deep5.精确侦听对象的某个属性 五、生命周期函数六、组件通信1.父传子2. 子传父 七、…

vue 总结

1.vue 的生命周期 1. es6 2. vue 基本属性指令 <template><div><!--<h1>vue基本指令的使用方式</h1><a :href"url">v-bind使用链接</a><img :src"srcUrl" /><div>解决闪烁问题<p v-cloak>{{…

【word】引用文献如何标注右上角

一、在Word文档中引用文献并标注在右上角的具体步骤如下 1、将光标移动到需要添加文献标注的位置&#xff1a; 2、在文档上方的工具栏中选择“引用”选项&#xff1a; 3、点击“插入脚注”或“插入尾注”&#xff1a; ①如果选择的是脚注&#xff0c;则脚注区域会出现在本页的…

Object.keys()的用法

1、语法 Object.keys(obj) 参数&#xff1a;要返回其枚举自身属性的对象 返回值&#xff1a;一个表示给定对象的所有可枚举属性的字符串数组 2、处理对象&#xff0c;返回可枚举的属性数组 let person {name:“张三”,age:25,address:“深圳”,getName:function(){}} Obj…

管理 PostgreSQL 中配置参数的各种方法

管理 PostgreSQL 中配置参数的各种方法 1. 概述 PostgreSQL提供了一个配置文件 postgresql.conf 让用户自定义参数。您可能需要更改一些参数来调整性能或在工作环境中部署 PostgreSQL 服务器。在这篇博文中&#xff0c;我们将探索管理这些参数的不同方法。 2. 以不同方式管理…

大语言模型系列-GPT-3

文章目录 前言一、GTP-3的改进二、GPT-3的表现总结 前言 《Language Models are Few-Shot Learners&#xff0c;2020》 前文提到GPT-2进一步提升了模型的zero shot能力&#xff0c;但是在一些任务中仍可能会“胡说”&#xff0c;GTP-3基于此提出了few shot&#xff0c;即预测…

PnP算法

PnP(Perspective-n-Point)是求解3D到2D点的对应方法。它描述了当知道n个3D空间点及其位置&#xff0c;如何估计相机的位姿。如果两张图像中的一张特征点3D位置已知&#xff0c;那么至少需要3个点对(以及至少一个额外验证点验证结果)就可以计算相机的运动。 PnP的应用范围很广比…

从 HPC 到 AI:探索文件系统的发展及性能评估

随着 AI 技术的迅速发展&#xff0c;模型规模和复杂度以及待处理数据量都在急剧上升&#xff0c;这些趋势使得高性能计算&#xff08;HPC&#xff09;变得越来越必要。HPC 通过集成强大的计算资源&#xff0c;比如 GPU 和 CPU 集群&#xff0c;提供了处理和分析大规模数据所需的…

LLM 加速技巧:Muti Query Attention

MQA 是 19 年提出的一种新的 Attention 机制&#xff0c;其能够在保证模型效果的同时加快 decoder 生成 token 的速度。在大语言模型时代被广泛使用&#xff0c;很多LLM都采用了MQA&#xff0c;如Falcon、PaLM、StarCoder等。 在介绍MQA 之前&#xff0c;我们先回顾一下传统的…

利用GPT开发应用001:GPT基础知识及LLM发展

文章目录 一、惊艳的GPT二、大语言模型LLMs三、自然语言处理NLP四、大语言模型LLM发展 一、惊艳的GPT 想象一下&#xff0c;您可以与计算机的交流速度与与朋友交流一样快。那会是什么样子&#xff1f;您可以创建哪些应用程序&#xff1f;这正是OpenAI正在助力构建的世界&#x…

Ethersacn的交易数据是什么样的(2)

分析 Raw Transanction RLP&#xff08;Recursive Length Prefix&#xff09;是一种以太坊中用于序列化数据的编码方式。它被用于将各种数据结构转换为二进制格式&#xff0c;以便在以太坊中传输和存储。RLP 是一种递归的编码方式&#xff0c;允许对复杂的数据结构进行编码。所…

word如何实现不同章节显示不同页眉

一、问题描述 写论文时遇到如下情形&#xff0c;第二章页眉跟第一章一样&#xff0c;如下图 二、解决方法 在第二章前一页空白处&#xff0c;选择依次布局→分隔符→下一页&#xff0c;如下图 双击第二章页眉&#xff0c;进入页眉编辑状态&#xff0c;点击链接到前一节按钮&a…

SOC设计:关于时钟门控的细节

有如下几个信号 输入信号 1、同步后的rstnsync_clk 2、时钟&#xff1a;clk 3、test_mode 4、软件控制信号&#xff1a;clk_sub_en 输出信号 1、clk_sub 功能&#xff1a;软件配置的使能信号clk_sub_en经过时钟clk 2拍同步处理后产生clk 域下的enable信号&#xff0c;然…

2024年腾讯云服务器99元一年,最新价格整理

腾讯云服务器99元一年是真的吗&#xff1f;真的&#xff0c;只是又降价了&#xff0c;现在只要61元一年&#xff0c;配置为2核2G3M轻量应用服务器&#xff0c;40GB SSD盘&#xff0c;腾讯云百科txybk.com分享腾讯云官方活动购买链接 https://curl.qcloud.com/oRMoSucP 活动打开…

Python编程实验六:面向对象应用

目录 一、实验目的与要求 二、实验内容 三、主要程序清单和程序运行结果 第1题 第2题 四、实验结果分析与体会 一、实验目的与要求 &#xff08;1&#xff09;通过本次实验&#xff0c;学生应掌握类的定义与对象的创建、类的继承与方法的覆盖&#xff1b; &#xff08;2…

鸿道Intewell-Win_V2.1.3_kyland软件版本发布说明

一、软件发布版本信息 版本号&#xff1a;V2.1.3_kyland 版本发布类型&#xff1a;trail试用版本 二、版本特点 适配 E211-1370&#xff08;J6412,8GB&#xff0c;256GB SSD&#xff09;设备 三、运行环境推荐 Intewell developer可以运行在windows7及windows10 64位 四、支…

程序员书单推荐:从入门到精通的必读之作

在程序员的职业生涯中&#xff0c;阅读技术书籍是不断学习和提升自我的重要途径。本文将为你推荐一系列从入门到精通的程序员书单&#xff0c;帮助你系统地掌握编程知识、提高技能水平&#xff0c;并在职业生涯中取得更大的进步。 一、入门篇 《Head First C语言》&#xff1…