和我一起刷leetcode-221-Maximal-Square

和我一起刷leetcode-221. Maximal Square

大家好,今天我们一起做Leetcode第221题 ,这是一道难度为Medium的题目。也是一道经典的动态规划练习题

题目描述

Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area.

Example:

Input: 

1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0

Output: 4

题目的大意是,给你一个二维数组matrix,其中有一些是0,有一些是1,找到其中最大的由1组成的正方形,并且返回它的面积。

动态规划三部曲

老规矩,动态规划三部曲开🐛!

开数组

这一题比较有技巧的地方,是如何定义动态规划数组dp[i][j], 因为最后要求的是在matrix这个数组中能够形成最大的正方形的面积,很容易就把dp[i][j]定义成:从[0][0]到[i][j]的矩形中,其中由1组成的最大的正方形面积。那么在这个定义下,我们最后要输出的是dp[-1][-1]。

这个定义初看起来很有道理,但是有一个很大的问题。考虑如下的例子:

1  1  1  1 
1  1  1  1
0  1  1  1

1  1  1  1 
1  1  1  1
1  1  0  1

我们考虑从第三行三个元素到第三行第四个元素,在这个定义下这两个例子中,第三行第三个元素的dp值都为4,但是第一个例子中,第三行第四个元素的dp值为9,第二个例子中则为4。 这是因为0的位置有不同,导致了一个可以形成正方形,另外一个不可以。

所以以上dp的定义是不可以的,我们需要另外想一个办法。

我们可以把dp[i][j]定义为:从[0][0]到[i][j]形成的矩形中,以[i][j]为右下角顶点的正方形,边长的最大值。 揣摩一下它的定义。

我们举一个例子:

1  1  0  0
0  1  1  1
1  1  1  1
0  1  1  1

对应的,我们写出它的dp数组:

1  1  0  0
0  1  1  1
1  1  2  2
0  1  2  3 

这样的话,我们最后输出的结果,应当是dp数组中的最大值,也就是matrix这个数组中最长的正方形边长。平方一下输出就是所求的结果。

知道了dp的定义以后,我们就照常开一个二维数组:

r = len(matrix)
c = len(matrix[0])
    
dp = [[0]*(c+1) for _ in range(r+1)]

状态转移方程

假设我们现在处理到了i,j这个位置,如果对应的matrix的元素为0,那么dp[i][j]直接等于0就可以了。因为右下角顶点是0,无论如何都不能组成全由1组成的正方形。

如果对应的matrix的元素为1,看一下下面这张图:
我是图

坐标(1,3)处的2意味着在这个位置有一个边长为2的正方形,同样,(1,2)和(2,2)也意味着这里有一个边长为2的长方形。那么为了形成一个边长为3的正方形,我们只需要在(2,3)位置处有1个1。所以(2,3)处的值为3.

接着看坐标(3,4)。(3,3)和(2,3)处的dp的值都是3,那么这两个点作为右下角,都可以形成边长为3的正方形。但是(2,4)处的dp值为1,这样,当(3,4)的值为1时,我们受最小的那个限制,也只能形成边长为2的正方形。

所以总结一下,当matrix对应元素为1时状态转移方程是:

dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]) + 1

找初始值

这一题很幸运的是,不需要额外进行初始值的寻找了。大家可以尝试一下把状态转移方程带入到边界中,恰好也可以用状态转移方程来算初始值。

实现

将以上三步结合一起来,要注意的是dp的index和matrix的index之间相差1,写程序的时候需要注意一下。另外最后我们需要找出dp数组中最大的数字,将它平方后就是matrix中最大的正方形的面积。我用的是max(map(max,dp)这种写法,比较简便。

可以直接在leetcode上提交的代码:

class Solution:
    def maximalSquare(self, matrix: List[List[str]]) -> int:
        if not matrix:
            return 0
        r = len(matrix)
        c = len(matrix[0])
        
        dp = [[0]*(c+1) for _ in range(r+1)]

        for i in range(1,r+1):
            for j in range(1,c+1):
                if matrix[i-1][j-1] == "1":
                    dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1]) + 1
                
        return max(map(max,dp))**2

这个解法大约能beat百分之九十的解法,应该算是在运行速度较快的解法中,最容易理解的一种了。

总结

这一题也是动态规划的经典习题,关键点在于如何定义dp数组。明天会继续带来更多的leetcode

支持

如果喜欢本文的话,欢迎关注我的微信公众号:老方刷题。谢谢您的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值