2d二进制矩阵中1的最大矩形

时间:2012-07-14 07:24:07

标签: arrays algorithm

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

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

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

  3. 示例:

    010101
    101001
    111101
    110101
    

    最大的矩形的面积为4(第3行,第5列,第3行,第4行)。我们还可以获得所有那些矩形吗?

6 个答案:

答案 0 :(得分:18)

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

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

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

  • 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)解决方案。通过查找单元格上方1个单元格的数量直到下一个0,DP每列工作.dp如下:

  • dp(r,0)= 0
  • 如果矩阵[r] [c] == 0 ,则
  • dp(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
  • 如果矩阵[r] [c] == 0 ,则
  • h(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如果矩阵[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的答案中对上述内容进行了调整 - 请参阅“本的妈妈”部分。在那篇文章中,0是树。该文章也有更好的格式。

答案 1 :(得分:2)

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

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

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计算该直方图中的最大矩形区域。

答案 2 :(得分:1)

我会尝试以下方法:

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

(2)对于每个连接的组件,查找最大矩形。

要做(2),我首先要寻找垂直矩形:找到每个连续(min_y,max_y)的最大可能宽度,从而找到该区域(迭代地,每行O(1),只需查看连接组件的那一行中的最小值/最大值1。 然后我将转置矩阵,并重复该过程。

BFS的总运行时间为O(MxN),然后是每个连接的组件的O(宽度^ 2 +高度^ 2),总计为O(MXN + M ^ 2 + N ^ 2)。

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

答案 3 :(得分:0)

**

//use this dynamic programming approach
//The problem can be reduced to finding the maximum rectangle area in a histogram, multiple times.
After each row, you calculate the histogram built until that row, and that calculate the maximum area rectangle in that histogram.

**

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;
    }


}

答案 4 :(得分:0)

另一种更简单的方法是使用两个临时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 :(得分:0)

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;
    }
}