【问题标题】:Largest rectangle of 1's in 2d binary matrix二维二进制矩阵中 1 的最大矩形
【发布时间】:2012-07-14 07:24:06
【问题描述】:

在0-1矩阵中求1的最大面积有问题。这个问题有两种情况:

  1. 要测量的区域是正方形。 DP很简单。

  2. 要测量的区域是矩形。我无法为此想出最佳解决方案。

例子:

010101
101001
111101
110101

最大的矩形面积为 4(第 3 行第 5 列,第 3、4 行还有一个)。我们也可以得到所有这些矩形吗?

【问题讨论】:

    标签: arrays algorithm


    【解决方案1】:

    我将逐步介绍一些增加难度/降低运行时复杂性的解决方案。

    首先,蛮力解决方案。生成每个可能的矩形。您可以通过迭代每对点 (r1,c1) (r2,c2) 来做到这一点,其中 r1 ≤ r2 和 c1 ≤ c2 (可以用 4 个 for 循环完成)。如果矩形不包含 0,则将该区域与目前找到的最大区域进行比较。这是一个 O(R^3C^3)。

    我们可以将有效矩形检查速度加快到 O(1)。我们通过执行 DP 来做到这一点,其中 dp(r, c) 存储矩形 ((1, 1), (r, c)) 中 0 的数量。

    • dp(r, 0) = 0
    • dp(0, c) = 0
    • dp(r,c) = dp(r−1,c)+dp(r,c−1)−dp(r−1,c−1)+(矩阵[r][c]?0: 1)

    那么((r1, c1), (r2, c2))中0的个数是

    • nzeroes(r1,c1,r2,c2) = dp[r2][c2]−dp[r1 −1][c2]−dp[r2][c1 −1]+dp[r1 −1][c1 −1]

    然后您可以通过 nzeroes(r1,c1,r2,c2) == 0 检查矩形是否有效。

    有一个使用简单 DP 和堆栈的 O(R^2C) 解决方案。 DP 每列工作,通过查找一个单元格上方的 1 个单元格直到下一个 0。dp 如下:

    • dp(r, 0) = 0
    • dp(r, c) = 0 如果矩阵[r][c] == 0
    • dp(r, c) = dp(r-1, c) + 1 否则

    然后您执行以下操作:

    area = 0
    for each row r:
      stack = {}
      stack.push((height=0, column=0))
      for each column c:
        height = dp(r, c)
        c1 = c
        while stack.top.height > height:
          c1 = stack.top.column
          stack.pop()
        if stack.top.height != height:
          stack.push((height=height, column=c1))
        for item in stack:
          a = (c - item.column + 1) * item.height
          area = max(area, a)
    

    也可以使用三个 DP 解决 O(RC) 中的问题:

    • h(r, c):如果我们从 (r, c) 开始往上走,我们在第一个 0 之前找到多少个 1 单元格?
    • l(r, c):我们可以将右下角位于 (r, c) 且高度为 h(r, c) 的矩形向左延伸多远?
    • r(r,c):我们可以将左下角位于 (r, c) 且高度为 h(r, c) 的矩形向右延伸多远?

    三个递归关系是:

    • h(0, c) = 0
    • h(r, c) = 0 如果矩阵[r][c] == 0
    • h(r, c) = h(r-1, c)+1 否则

    • l(r, 0) = 0

    • l(r, c) = c-p if matrix[r-1][c] == 0
    • l(r, c) = min(l(r - 1, c), c - p) 否则

    • r(r,C+1) = 0

    • r(r,c) = p-c if matrix[r-1][c] == 0
    • r(r,c) = min(r(r-1, c), p-c) 否则

    其中 p 是前一个 0 的列,因为我们从左右填充 l,从左右填充 r。

    那么答案是:

    • max_r,c(h(r, c) ∗ (l(r, c) + r(r, c) − 1))

    这是因为观察到最大的矩形总是在所有四个边上都接触到 0(考虑到边缘被 0 覆盖)。通过考虑至少顶部、左侧和右侧接触 0 的所有矩形,我们覆盖了所有候选矩形。生成每个可能的矩形。您可以通过迭代每对点 (r1,c1) (r2,c2) 来做到这一点,其中 r1 ≤ r2 和 c1 ≤ c2 (可以用 4 个 for 循环完成)。如果矩形不包含 0,则将该区域与目前找到的最大区域进行比较。

    注意:我根据我写的答案 here 改编了以上内容 - 请参阅“Ben 的妈妈”部分。在那篇文章中,0 是树。该文章的格式也更​​好。

    【讨论】:

    • 非常感谢。很好的解决方案。
    【解决方案2】:

    我会尝试以下方法:

    (1) 将矩阵分解为连通分量(通过 BFS)。

    (2) 对于每个连通分量,寻找最大矩形。

    要执行 (2),我将首先查找垂直矩形:查找每个连续 (min_y, max_y) 的最大可能宽度,因此查找面积(迭代地,每行 O(1),只需查看连接组件的该行中 1 的最小值/最大值)。 然后我会转置矩阵,并重复这个过程。

    BFS 的总运行时间为 O(MxN),然后每个连接的组件为 O(width^2 + height^2),总共为 O(MXN + M^2 + N^2)。

    我想知道什么是渐近最优解。

    【讨论】:

    • 你能描述一下吗?
    【解决方案3】:

    这个问题可以简化为多次在直方图中找到最大矩形区域。

    在每一行之后,您计算在该行之前构建的直方图,然后计算该直方图中的最大面积矩形。

    int maximalRectangle(vector<vector<char> > &mat) {
        int rows=mat.size();
        if(rows==0)return 0;
        int columns = mat[0].size();
    
        int temp[columns];
        for(int i=0;i<columns;i++){
            temp[i] = mat[0][i]-'0';
        }
    
        int maxArea=0;
        maxArea = max(maxArea,maxUtil(temp,columns));
        // cout<<"before loop\n";
        // print1d(temp,columns);
        for(int i=1;i<rows;i++){
            for(int j=0;j<columns;j++){
                temp[j] = (mat[i][j]-'0')?temp[j]+1:0;
            }
            // cout<<"after iteration : "<<i<<endl;
            // print1d(temp,columns);
            maxArea = max(maxArea,maxUtil(temp,columns));
            // cout<<"maxarea = "<<maxArea<<endl;
        }
        return maxArea;
    }
    

    temp 是每一步的直方图,maxutil 计算该直方图中的最大矩形面积。

    【讨论】:

      【解决方案4】:

      另一种更简单的方法是使用两个临时 M x N 数组来计算矩形的长度(行和列) - 即连续 1 的计数。遍历两个临时矩阵以找到最大重复长度(行和列)。

      这是相同的代码。

      int GetMaxRectangularArea(vector<vector<int>> & matrix, int nRows, int nCols)
      {
          vector<vector<int>>  rowLengths(nRows, vector<int>(nCols));
          vector<vector<int>>  colLengths(nRows, vector<int>(nCols));
      
          // initialize first column of rowLengths with first column of matrix
          for (int i = 0; i < nRows; i++) {
              rowLengths[i][0] = matrix[i][0];
          }
      
          // initialize first row of colLengths with first row of matrix
          for (int j = 0; j < nCols; j++) {
              colLengths[0][j] = matrix[0][j];
          }
      
          // Compute row wise length of consecutive 1's in rowLengths
          for (int i = 0; i < nRows; i++) {
              for (int j = 1; j < nCols; j++) {
                  if (matrix[i][j] == 1) {
                      rowLengths[i][j] = 1 + rowLengths[i][j - 1];
                  }
                  else {
                      rowLengths[i][j] = 0;
                  }
              }
          }
      
          // Compute column wise length of consecutive 1's in colLengths
          for (int j = 0; j < nCols; j++) {
              for (int i = 1; i < nRows; i++) {
                  if (matrix[i][j] == 1) {
                      colLengths[i][j] = 1 + colLengths[i- 1][j];
                  }
                  else {
                      colLengths[i][j] = 0;
                  }
              }
          }
      
          // Now traverse the rowLengths array to find max length sub array
          int maxArea = 0;
      
          for (int j = nCols - 1; j >= 0; j--) {
              int currentArea = 0;
              int currentMax = -1;
              int repeats = 1;
      
              for (int i = nRows - 1; i >= 0; i--) {
                  if (rowLengths[i][j] != currentMax) {
                      if (currentMax != -1) {
                          currentArea = currentMax * repeats;
      
                          if (currentArea > maxArea) {
                              maxArea = currentArea;
                          }
                      }
      
                      currentMax = rowLengths[i][j];
                      repeats = 1;
                  }
                  else {
                      repeats++;
                  }
              }
      
              currentArea = currentMax * repeats;
      
              if (currentArea > maxArea) {
                  maxArea = currentArea;
              }
          }
      
          for (int i = nRows - 1; i >= 0; i--) {
              int currentArea = 0;
              int currentMax = -1;
              int repeats = 1;
      
              for (int j = nCols - 1; j >= 0; j--) {
                  if (colLengths[i][j] != currentMax) {
                      if (currentMax != -1) {
                          currentArea = currentMax * repeats;
      
                          if (currentArea > maxArea) {
                              maxArea = currentArea;
                          }
                      }
      
                      currentMax = colLengths[i][j];
                      repeats = 1;
                  }
                  else {
                      repeats++;
                  }
              }
      
              currentArea = currentMax * repeats;
      
              if (currentArea > maxArea) {
                  maxArea = currentArea;
              }
          }
      
          return maxArea;
      } 
      

      【讨论】:

        【解决方案5】:
        class GfG{
            public int maxArea(int a[][],int m,int n){
                if(a==null || m==0 || n== 0) return 0;
                m = a.length;
                n = a[0].length;
                int dp[] = new int[n+1];
                int height[] = new int[n];
                int p = 0;
                dp[p] = -1;
                int ans = 0;
                //System.out.println("1 ");
                for(int i = 0;i<m;i++){
                    for(int j = 0;j<n;j++){
                        if(a[i][j]==1){
                            height[j] += a[i][j];
                        }
                        else{
                            height[j] = 0;
                        }
                    }
                    p= 0;
                    //System.out.println("2 ");
                   for(int j = 0;j<n;j++){
                      while(p>0 && height[j] < height[dp[p]]){
                          int start =  dp[p-1];
                          ans = Math.max(ans,(j-start-1)*height[dp[p]]);
                          p--;
                          //System.out.println("1 ");
                      } 
                      dp[++p] = j;
                   }
                }
                return ans;
            }
        }
        

        【讨论】:

        • 考虑到这是一个老问题,请解释您的代码,并解释它与已有的其他答案有何不同。
        【解决方案6】:

        使用这种动态编程方法

        • 可以将问题简化为多次查找直方图中的最大矩形区域。
        • 在每一行之后,您计算在该行之前构建的直方图,并计算该直方图中的最大面积矩形。

        **

        import java.util.Scanner;
        
        public class LargestRectInAmatrix {
            static int row, col, matrix[][];
            static int maxArea = 0;
            static int barMatrix[];
            public static void main(String[] args) {
                Scanner sc = new Scanner(System.in);
                row = sc.nextInt();
                col = sc.nextInt();
                matrix = new int[row][col];
                barMatrix = new int[col];
                for(int i = 0; i < row ; i++) {
                    for(int j = 0 ; j < col ; j++) {
                        matrix[i][j] = sc.nextInt();
                    }
                }
                startSolution();
                System.out.println(maxArea);
            }
            private static void startSolution() {
                for(int i = 0 ; i < row ; i++) {
                    for(int j = 0 ; j < col ; j++) {
                    if(matrix[i][j] == 0) {
                        barMatrix[j] = 0;
                    }
                    else
                        barMatrix[j]=barMatrix[j]+matrix[i][j];
                    }
                    int area = calculateArea(0, col-1);
                    if(area > maxArea) {
                        maxArea = area;
                    }
                }
                
            }
            private static int calculateArea(int l,int h)
            {
                if(l > h) {
                    return Integer.MIN_VALUE;
                }
                if(l == h) {
                    return barMatrix[l];
                }
                int u = calMinimumIndex(l,h);
                return (max(calculateArea(l, u-1), calculateArea(u+1, h), barMatrix[u] * (h - l + 1)));
            }
            private static int max(int a, int b, int c) {
                if(a > b) {
                    if(a > c) {
                        return a;
                    }
                    else
                        return c;
                }
                else
                    if(b > c) {
                        return b;
                    }
                    else
                        return c;
            }
            private static int calMinimumIndex(int l, int h) {
                int min=Integer.MAX_VALUE;
                int min_index = 0;
                for(int i = l ; l <= h ; i++) {
                    if(barMatrix[i] < min){
                        min = barMatrix[i];
                        min_index = i;
                    }
                }
                return min_index;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2021-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-21
          • 1970-01-01
          相关资源
          最近更新 更多