边界都是1的最大正方形大小

给定一个0和1组成的N*M矩阵,任务是找到边框全是1的最大正方形的边长。通过预处理矩阵,可以降低时间复杂度到O(N^3),空间换时间。预处理包括记录每个单元格右侧和下方连续1的数量。以此来判断是否存在符合条件的正方形。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

  • 给定一个N*M的矩阵matrix, 在这个矩阵中, 只有0和1两种值, 返回边框全是1的最大正方
    形的边长长度。
    例如:
    0 1 1 1 1
    0 1 0 0 1
    0 1 0 0 1
    0 1 1 1 1
    0 1 0 1 1
    其中, 边框全是1的最大正方形的大小为4*4, 所以返回4

问题分析

  • 首先明确一个问题,便是如果枚举一个N*M的矩阵中的所有的子矩阵,时间复杂度为O(NM)*O(NM),因为一个矩形可由左上角与右下角决定,枚举左上角为O(NM),枚举左上角为O(NM)
    那么如果枚举这个矩阵中所有的正方形呢?一个正方形可由左上角的点及其边长所决定,那么时间复杂度为O(NM)*min(N*M),因为边长最多有min(N,M)种情况。
  • 所以对于该题,有两种解法:
  • 方法1:枚举所有的正方形,然后检查四个边,所以当M=N时,这是O(N^4)时间复杂度
  • 方法2:采用预处理矩阵的方法,同样也是枚举所有的正方形,但是判断该正方形是否符合规则是,是O(1)的时间复杂度,所以当M=N时,这是O(N^3)时间复杂度。
    这是以空间换时间的做法。
    那么如何预处理矩阵呢?
    • 用与原矩阵同样大小的两个矩阵,一个为right,一个为down,right[i][j]表示m[i][j]的右边有多少个连续的1,包括自身;down[i][j]表示m[i][j]的下边有多少个连续的1,包括自身。从右到左,从下到上依次填好两个矩阵。
    • 对于以[row,col]为左上角,length作为边长的正方形,要想四个边全为1,只有满足如下条件:row,col代表左上方的位置,要求左上方处下边最少有连续的length个1,右边最少有连续的length个1;[row + length - 1][col]代表左下角,要求该点右边最少有连续的length个1;[row][col + length - 1]代表右上角,要求该点下边最少有连续的length个1;这样便确立了四个边都符合规则。

经验教训:

  • 预处理矩阵的技巧(举例:如果要求矩阵中某个矩形的和,那么应该如何预处理矩阵)
    • 以当前点作为矩形右下角,原矩阵左上角作为新的左上角,求该矩阵的和,那么sum[][]便构造得到,根据sum矩阵,可以得到任意矩阵的和
  • 关于该题的复杂度,以及如何更新两个矩阵

代码实现

package advanced_class_04;

public class MyCode_05_MaxOneBorderSize {

    //根据m矩阵,设置right与down矩阵,right[i][j]表示m[i][j]的右边有多少个连续的1,包括自身
    //down[i][j]表示m[i][j]的下边有多少个连续的1,包括自身
    public static void setRightAndDown(int[][] m, int [][] right, int[][] down) {
        int rowNum = m.length;
        int colNum = m[0].length;
        //初始化右下角位置
        right[rowNum - 1][colNum - 1] = m[rowNum - 1][colNum - 1];
        down[rowNum - 1][colNum - 1] = m[rowNum - 1][colNum - 1];
        //初始化最后一行
        for (int col = colNum - 2; col >= 0; col--) {
            if (m[rowNum - 1][col] == 1) {
                right[rowNum - 1][col] = right[rowNum - 1][col + 1] + 1;
                down[rowNum - 1][col] = 1;
            }
        }
        //初始化最后一列
        for (int row = rowNum - 2; row >= 0; row--) {
            if (m[row][colNum - 1] == 1) {
                right[row][colNum - 1] = 1;
                down[row][colNum - 1] = down[row + 1][colNum - 1] + 1;
            }
        }
        //其他位置,从右向左,从下到上设置值
        for (int row = rowNum - 2; row >= 0; row--) {
            for (int col = colNum - 2; col >= 0; col--) {
                if (m[row][col] == 1) {
                    right[row][col] = right[row][col + 1] + 1;
                    down[row][col] = down[row + 1][col] + 1;
                }
            }
        }
    }

    public static int getMaxSize(int[][] m) {
        int[][] right = new int[m.length][m[0].length];
        int[][] down = new int[m.length][m[0].length];
        setRightAndDown(m, right, down);

        for (int length = Math.min(m.length, m[0].length); length >= 1; length--) {
            //对于每个边长,看是否存在以该值作为边长的矩阵,满足四周全为1
            //因为要找最大边长,所以从大到小找
            if (hasSizeOfBorder(length, right, down)) {
                return length;
            }
        }
        return 0;
    }

    //该函数实现传入一个边长值,根据right与down矩阵,判断是否存在正方形满足四周全为1
    public static boolean hasSizeOfBorder(int length, int[][] right, int[][] down) {
        for (int row = 0; row + length - 1 <= right.length - 1; row++) {
            for(int col = 0; col + length - 1 <= right[0].length - 1; col++) {
                //row,col代表左上方的位置,要求左上方处下边最少有连续的length个1,右边最少有连续的length个1
                //[row + length - 1][col]代表左下角,要求该点右边最少有连续的length个1
                //[row][col + length - 1]代表右上角,要求该点下边最少有连续的length个1
                //这样便确立了四个边
                if (right[row][col] >= length && down[row][col] >= length
                        && right[row + length - 1][col] >= length
                        && down[row][col + length - 1] >= length) {
                    return true;
                }
            }
        }
        return false;
    }


    public static int[][] generateRandom01Matrix(int rowSize, int colSize) {
        int[][] res = new int[rowSize][colSize];
        for (int i = 0; i != rowSize; i++) {
            for (int j = 0; j != colSize; j++) {
                res[i][j] = (int) (Math.random() * 2);
            }
        }
        return res;
    }

    public static void printMatrix(int[][] matrix) {
        for (int i = 0; i != matrix.length; i++) {
            for (int j = 0; j != matrix[0].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        int[][] matrix = generateRandom01Matrix(7, 8);
        printMatrix(matrix);
        System.out.println(getMaxSize(matrix));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值