力扣题解系列:1139. 最大的以 1 为边界的正方形

题目:1139. 最大的以 1 为边界的正方形

题目描述:

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

示例:

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

提示:

1)`1 <= grid.length <= 100`
(2)`1 <= grid[0].length <= 100`
(3)`grid[i][j]01`

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/gu-piao-de-zui-da-li-run-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

(1)暴力法

当实在没有更好的办法时,暴力法也可以试一试。
思路
从大到小,遍历所有可能的正方形,看是否满足条件,第一个满足条件的正方形即符合要求,返回边长的平方即可。
代码:

class Solution:
    def largest1BorderedSquare(self, grid) -> int:
        r, c = len(grid), len(grid[0])
        # 这里是为了防止给出的grid长和宽不相等

        def check(x1, y1, x2, y2):
            # 检验选中的正方形是否满足题目要求
            # 其中输入条件是正方形左上角和右下角的后一个点的点的坐标(即正方形右下角坐标应该是(x2-1,y2-1))
            for x in range(x1, x2):
                if grid[x][y1] != 1 or grid[x][y2-1] != 1:  # 边框值是否为1
                    return False
            for y in range(y1, y2):
                if grid[x1][y] != 1 or grid[x2-1][y] != 1:  # 边框值是否为1
                    return False
            return True
             
        for k in range(min(r, c), 0, -1):
            for x1 in range(r):
                if x1 + k > r:  # 正方形越界,退出
                    break
                for y1 in range(c):
                    x2, y2 = x1 + k, y1 + k  # 得到正方形右下角的下一个点坐标
                    if y2 > c:  # 正方形越界,退出
                        break
                    if check(x1, y1, x2, y2):  # 检查得到的正方形是否满足条件
                        return k ** 2  # 若满足条件则返回面积
        return 0  # 没有找到符合条件的正方形,返回0

注意
(1)由于是从最大正方形开始遍历,找到的符合题目要求的第一个正方形即为满足条件的最大正方形
(2)暴力法的时间复杂度O(n^4)(n是输入边长).
(3)这里考虑了输入长宽不相等的情况,若题目给定的grid是正方形,可不考虑。

(2)动态规划

思路:
(1)建立dp数组。
三维数组dp第一和第二维分别比原数组的长度大1,便于处理边界。第三维用两个数分别记录给定数组在当前元素位置时,左边(含本身)和上方(含本身)连续的1的个数。
(2)遍历dp数组。
记录当前元素左边和上边连续1的个数的最小值作为边长的可能值,若当前边长可能值比目前输出中存储的边长要大,则检查另外两条边在当前边长可能值下是否满足条件。若满足条件则更新输出,否则减小当前边长可能值的大小,若减一后的边长可能值仍然大于目前输出的边长,继续判断另外两条边。。。
(3)第二步的遍历也是从大到小,满足条件的第一个正方形即为满足要求的最大正方形。
代码:

class Solution:
    def largest1BorderedSquare(self, grid) -> int:
        r,c = len(grid), len(grid[0])
        dp = [[[0,0] for j in range(c+1)] for i in range (r+1)]
        # dp数组初始化
        for i in range(r):
            for j in range(c):
                if grid[i][j] == 1:  
                    # 用一个三维矩阵记录原矩阵每个元素的左边和上边(包括)有多少个连续的1
                    # 其中矩阵的维度比原始矩阵大1,可以省略边界条件判断(j+1可能越界)
                    dp[i+1][j+1][0] = 1 + dp[i+1][j][0]  # 左边连续的1
                    dp[i+1][j+1][1] = 1 + dp[i][j+1][1]  # 上边连续的1
        ans = 0
        for i in range(1,r+1):
            for j in range(1,c+1):  # 遍历记录矩阵
                height = min(dp[i][j])  # 记录当前元素左边和上边连续1的个数的最小值作为边长的可能值
                while height > ans:  
                    if min(dp[i][j][0],dp[i][j][1],dp[i-height+1][j][0],dp[i][j-height+1][1]) >= height:
                        # 检查另外两条边长是否符合要求
                        # 若符合要求更新边长
                        ans = height
                        break
                    else:
                        # 若另两条边长不满足最大边长,求出另外两条边长最大的连续长度
                        height -= 1
        return ans*ans

注意:
(1)dp矩阵的维度比原始矩阵大1,可以省略边界条件判断(i+1和j+1可能越界)
(2)关于检查另外两条边:
假设当前可能边长为height,当前输出边长为ans,遍历dp数组的位置在[i,j],即min(dp[i][j])=height,则要知道边长为height的正方形是否满足条件,需要判断min(dp[i][j+k-1])是否大于ans,若不满足,还不能立即退出,需要在满足可能边长大于当前输出边长的情况下,看能否找到比当前输出边长更大的正方形,也就是这里需要一个循环对应代码中:

while height > ans:  
    if min(dp[i][j][0],dp[i][j][1],dp[i-height+1][j][0],dp[i][j-height+1][1]) >= height:
        # 检查另外两条边长是否符合要求
        # 若符合要求更新边长
        ans = height
        break
    else:
        # 若另两条边长不满足最大边长,求出另外两条边长最大的连续长度
        height -= 1

(3)因为当前可能边长height是会改变的故在循环中的判断语句中需要判断dp数组中的四个点,不能省略前两个。即:

if min(dp[i][j][0],dp[i][j][1],dp[i-height+1][j][0],dp[i][j-height+1][1]) >= height:

(4)动态规划方法的时间复杂度为O(n^3)
暴力法代码参考
动态规划代码参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值