LeetCode算法系列:85. Maximal Rectangle

题目描述:

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

算法实现:

这个问题关键在于思路,如果人为判断是怎么做的呢,将我们的想法分解步骤化一下,一行一行考虑,一个位置一个位置考虑。

先考虑第一行:

LeetCode算法系列:85. Maximal Rectangle_第1张图片

对于0的位置我们不用考虑,到第一个1和第二个1它们都是宽度为1高度为1的矩形,相应的面积为1

第二行:

LeetCode算法系列:85. Maximal Rectangle_第2张图片

  • 先考虑第一个1,在这一列当前位置1的有效高度为2,宽度为1,那么面积为2;
  • 考虑第二个1,同样有效高度为2,高度为2的有效宽度为1,面积为2;值得注意的是高度比这种有效高度小的情况,这种较小的高度必定是某一个位置的有效高度的最大值,所以不用考虑。
  • 考虑第三个1,有效高度为1,有效高度为1的矩阵的宽度,左侧可以延伸到索引为2处,右侧可以延伸到索引为4处,宽度为4 - 2 + 1;
  • ......

同样第三行和第四行:

LeetCode算法系列:85. Maximal Rectangle_第3张图片LeetCode算法系列:85. Maximal Rectangle_第4张图片

 

依照这个思路我们可以细化得到一种详细的算法,通过维护三个长度与矩阵行长度等长的数组,h,l,r分别记录当前位置的矩形的高度,左侧边界索引,右侧边界索引+1(方便我们r-l就是矩阵的长);

我们直接扫描每一行,对每一行的第j个位置

  • 如果当前位置矩阵的字符为‘0’,我们直接将高度h[j]置为零,l,r值其实可以设置的比较随意,但是考虑到当前行某个位置的l和r和上一行有关系(比如例子中第三行的第三个1对应的矩阵),如果字符为0,将l设置为0,将r设置为行长度n
  • 如果当前位置矩阵的字符为1
    • 对于h,直接在原来基础上加一即可,即此列此处的有效高度加一(h[j] ++)
    • 对于l,  用now_l是当前列能够延伸到的最左侧的索引,而l[j]是上一行包含j列有效连续1区段的能够延伸到最左侧的索引
      该列的此处的有效高度要加上这个位置的1,而有效宽度的左侧要既满足这一行要求又要满足上一行要求,具体的
      • l[j] = max(l[j],now_l);
    • 同样的对于r有:
      • r[j] = min(r[j],now_r);
    • 如果得到当前位置的l,r,h后可以得到到当前位置的矩形最大值
      • max(res, (r[j] - l[j])*h[j]);

比如题目中的例子,中各个位置的(l,r,h)可以写为:

(0,1,1),(0,5,0),(2,3,1),(0,5,0),(0,5,0)
(0,1,2),(0,5,0),(2,3,2),(2,5,1),(2,5,1)
(0,1,3),(0,5,1),(2,3,3),(2,5,2),(2,5,2)
(0,1,4),(0,5,0),(0,5,0),(3,4,3),(0,5,0)
//动态规划的方法,动态规划的不是面积,而是当前位置某种情况的面积
class Solution {
public:
    int maximalRectangle(vector>& matrix) {
        if(matrix.empty())return 0;
        int m = matrix.size();
        int n = matrix[0].size();
        int res = 0;
        vector h(n,0);
        vector l(n,0);
        vector r(n,n);
        for(int i = 0; i < m; i ++){
            //now_l和now_r分别记录当前行到当前位置的连续1序列的开始和结束
            int now_l = 0, now_r = n;
            for(int j = n - 1; j >= 0; j --){
                if(matrix[i][j] == '1'){
                    r[j] = min(r[j],now_r);
                }
                else{
                    r[j] = n;
                    now_r = j;
                }
            }
            for(int j = 0; j  < n; j ++)
            {                  
                if(matrix[i][j] == '1'){
                    //h[j]记录的是第i行第j列的有效最高高度
                    h[j] ++;
                    //now_l是当前列能够延伸到的最左侧的索引,而l[j]是上一行包含j列有效连续1区段的能够延伸到最左侧的索引
                    //因为这个位置是1,所以该列的有效高度要加上这个1,而有效宽度的左侧要既满足这一行要求又要满足上一行要求
                    l[j] = max(l[j],now_l);
                    res = max(res, (r[j] - l[j])*h[j]);
                    // cout << i << "," << j << ": " << r[j] << "," << l[j] << "," << h[j] << "," << res << endl;
                }
                else{
                    h[j] = 0;
                    //如果当前矩阵中的字符为0,那么l和r的值为多少意义不大,因为h为0,对应的面积一定为0,将其分别定义为0和行长度
                    l[j] = 0;
                    now_l = j + 1;
                }
            }
        }
        return res;
    }
};

 

你可能感兴趣的:(刷题系列(LeetCode,牛客等))