85. Maximal Rectangle

本文深入探讨了在二维二进制矩阵中寻找包含唯一1的最大矩形面积的问题,提供了两种解决方案:一是通过直方图算法,二是使用动态规划优化。详细讲解了算法原理,包括如何更新高度和宽度,以及代码实现。

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

Given a 2D binary matrix filled with 0’s and 1’s, find the largest rectangle 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: 6

解法

其实n^3的解法也过了……

参考:https://www.cnblogs.com/lupx/archive/2015/10/20/leetcode-85.html

解法一:同84

height[i]可以看成是前i行的每一列的1堆叠成一个直方图,每一列看成一个bar,那么height[i][j]就是第j个bar的高度
需要注意的是,每个bar有可能被0中断,这时候高度要重新算

class Solution(object):
    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        m = len(matrix)
        if m==0:
            return 0
        n = len(matrix[0])
        if n==0:
            return 0
        ans = 0
        for i in xrange(m):
            matrix[i] = map(lambda x:ord(x)-ord('0'), matrix[i])
        heights = [0]*n
        ans = 0
        for i in xrange(m):
            stack = []
            for j in xrange(n):
                heights[j] = (heights[j]+matrix[i][j]) if matrix[i][j] else 0
                while len(stack) and heights[stack[-1]]>heights[j]:
                    k = stack.pop()
                    b = len(stack) and stack[-1]+1 or 0
                    ans = max(ans, heights[k]*(j-b))
                stack.append(j)
            while len(stack):
                k = stack.pop()
                b = len(stack) and stack[-1]+1 or 0
                ans = max(ans, heights[k]*(n-b))
        return ans

解法二:DP

简而言之是上一个版本的优化,上一个版本,每更新一行,stack都清空重新算一下
但是实际上,增加一行之后的直方图的宽度和上次次计算出来的宽度之间是有联系的
我们假设遍历了i行,而heights[j]和width[j]分别是第j个bar的高度,以及以第j个bar的高度为高的矩形的最大宽度
我们想想width[j]是怎么来的,它代表的是一个区间[s,t)长度,其中在这个区间内的bar的高度都不低于heights[j]
当我们增加一行时,会出现以下的情况:

  • martix[i+1][j]为0:这时候heights[j]突然降为0,那么理论上说[s,t)将会扩大到[0,n),但是这种情况下面积一定是0
  • martix[i+1][j]为1:那么heights[j]会加1,第i+1行中:
    1. 如果列在区间[s,t)里的元素都是1,那么这个区间不会变【在区间外的就算加了1也挤不进来】
    2. 如果存在 k ∈ [ s , t ) k\in[s,t) k[s,t)是0,那么不幸,heights[k]降为0,一定不会在[s,t)里了,所以我们可以知道新的[s,t)要怎么来了:
      新的s’是 [s,j] j最左边的1的位置(就是说令[s’,j]都是1的最小s’),而新的t’是 [j+1,t] j右边第一个0的位置

      注意,新的区间一定是老区间的子集,因为【在区间外的就算加了1也挤不进来】

所以DP是指用DP来寻找s和t
算法如下:
left[j]代表每个j对应的s,right[j]代表每个j对应的t
LR分别对应遍历到j时,j左边第一个0和右边第一个0的位置

  1. 如果matrix[i+1][j]==0:
    left[j]=0right[j]=n
    L=j——从左向右遍历时
    R=j——从右向左遍历时
  2. 如果matrix[i+1][j]==1,显然:
    left[j]=max(L+1,left[j])
    right[j]=min(R,right[j])

代码如下:

class Solution(object):
    def maximalRectangle(self, matrix):
        """
        :type matrix: List[List[str]]
        :rtype: int
        """
        m = len(matrix)
        if m==0:
            return 0
        n = len(matrix[0])
        if n==0:
            return 0
        h = [0]*n
        ans = 0
        l = [-1]*n
        r = [n]*n
        for i in xrange(m):
            R = n
            for j in xrange(n-1,-1,-1):
                if matrix[i][j]=='0':
                    r[j] = n
                    R = j
                else:
                    r[j] = min(r[j],R)
            L = -1
            for j in xrange(n):
                if matrix[i][j]=='1':
                    h[j] += 1
                    l[j] = max(l[j],L+1)
                else:
                    h[j] = 0
                    l[j] = 0
                    L = j
                ans = max(ans, h[j]*(r[j]-l[j]))
                
        return ans

不幸并没有比stack快很多……= =

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值