16

There is a problem to find the maximum area of the 1 in the 0-1 matrix. In this problem there are two cases:

  1. area to be measure is of shape square. that's simple one by DP.

  2. area to be measure is of the shape of rectangle. i am not able to think a optimal solution for this.

Example:

010101
101001
111101
110101

The largest rectangle has an area of 4 (3rd row , 5th column and one more in 3rd,4th row). Can we also get all those rectangle ?

RATHI
  • 5,129
  • 8
  • 39
  • 48

6 Answers6

28

I'll step through a few solutions of increasing difficulty / decreasing runtime complexity.

First, a brute force solution. Generate every possible rectangle. You can do this by iterating through every pair of points (r1,c1) (r2,c2) with r1 ≤ r2 and c1 ≤ c2 (can be done with 4 for loops). If a rectangle does not contain a 0, you compare the area to the largest area found so far. This is an O(R^3C^3).

We can speed up the valid rectangle check to O(1). We do this by doing a DP where dp(r, c) stores the number of 0's in the rectangle ((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)+(matrix[r][c]?0:1)

Then the number of 0's in ((r1, c1), (r2, c2)) is

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

You can then check if a rectangle is valid by nzeroes(r1,c1,r2,c2) == 0.

There is an O(R^2C) solution for this using a simple DP and a stack. The DP works per column, by finding the number of 1 cells above a cell until the next 0. The dp is as follows:

  • dp(r, 0) = 0
  • dp(r, c) = 0 if matrix[r][c] == 0
  • dp(r, c) = dp(r-1, c) + 1 otherwise

You then do the following:

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)

It is also possible to solve the problem in O(RC) using three DP’s:

  • h(r, c): if we start at (r, c) and go upwards, how many 1 cells do we find before the first 0?
  • l(r, c): how far left can we extend a rectangle with bottom-right corner at (r, c) and height h(r, c)?
  • r(r,c): how far right can we extend a rectangle with bottom-left corner at (r, c) and height h(r, c)?

The three recurrence relations are:

  • h(0, c) = 0
  • h(r, c) = 0 if matrix[r][c] == 0
  • h(r, c) = h(r-1, c)+1 otherwise

  • 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) otherwise

  • 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) otherwise

where p is the column of the previous 0 as we populate l from left-right and r from right-left.

The answer is then:

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

This works because of the observation that the largest rectangle will always touch a 0 (considering the edge as being covered in 0's) on all four sides. By considering all rectangles with at least top, left and right touching a 0, we cover all candidate rectangles. Generate every possible rectangle. You can do this by iterating through every pair of points (r1,c1) (r2,c2) with r1 ≤ r2 and c1 ≤ c2 (can be done with 4 for loops). If a rectangle does not contain a 0, you compare the area to the largest area found so far.

Note: I adapted the above from an answer I wrote up here - refer to the section "Ben's Mom". In that writeup, the 0's are trees. That writeup also has better formatting.

adrtam
  • 6,991
  • 2
  • 12
  • 27
moinudin
  • 134,091
  • 45
  • 190
  • 216
1

I would try the following:

(1) Decompose the matrix into connected components (through BFS).

(2) For each connected component, look for the maximal rectangle.

To do (2), I would first look for vertical rectangles: Find the maximal possible width for each consecutive (min_y, max_y), and therefore the area (iteratively, in O(1) per row, just by looking at the min/max of 1's in that row of the connected component). Then I would transpose the matrix, and repeat the process.

The total running time is O(MxN) for BFS, then O(width^2 + height^2) for each connected componenet, for a total of O(MXN + M^2 + N^2).

I wonder what's the asymptotically optimal solution though.

Guy Adini
  • 5,188
  • 5
  • 32
  • 34
1

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.

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 is the histogram in each step and maxutil calculates the max rectanglular area in that histogram.

Yash
  • 5,225
  • 4
  • 32
  • 65
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;
    }
}
Siddharth
  • 9,349
  • 16
  • 86
  • 148
0

Another simpler approach is to use two temp M x N arrays to compute the length of rectangles (row and column wise) - ie count of consecutive 1's then. Traverse the two temp matrices to find max repeating lengths (row and column wise).

Here is the code for the same.

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;
} 
Shyam
  • 13
  • 3
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;
    }
}
HeadAndTail
  • 804
  • 8
  • 9
  • Considering it is an old question, explain your code, and explain how it is different from the other answers already there. – Nic3500 Dec 27 '17 at 05:45