Dynamic Programming | Set 27 (Maximum sum rectangle in a 2D matrix)


cracking-the-coding-interview chap18-Q12.java

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.

This problem is mainly an extension of Largest Sum Contiguous Subarray for 1D array.


The naive solution for this problem is to check every possible rectangle in given 2D array. This solution requires 4 nested loops and time complexity of this solution would be O(n^4).

The idea is to fix the left and right columns one by one and find the maximum sum contiguous rows for every left and right column pair.

We basically find top and bottom row numbers (which have maximum sum) for every fixed left and right column pair. To find the top and bottom row numbers, calculate sun of elements in every row from left to right and store these sums in an array say temp[]. So temp[i] indicates sum of elements from left to right in row i. If we apply Kadane’s 1D algorithm on temp[], and get the maximum sum subarray of temp, this maximum sum would be the maximum possible sum with left and right as boundary columns. 
Explanation from http://www.algorithmist.com/index.php/UVa_108
  • First, calculate the vertical prefix sum for all columns (an O(n2) algorithm).
  • Second, assume that the maximum sub-array will be between row a and row b, inclusive. There are only O(n2ab pairs such that a < b. Try each of them.
  • Since we already have the vertical prefix sum for all columns, the sum of elements in arr[a..b][c] for column c can be computed in O(1) time. This allows us to imagine each column sum as if it is a single element of a one-dimensional array across all columns (one dimensional array with one row and n columns).
  • There’s an O(n) algorithm to compute the maximum sub-array for a one-dimensional array, known as Kadane’s Algorithm.
  • Applying the Kadane’s algorithm inside each a and b combination gives the total complexity of O(n3)
void findMaxSum(int M[][COL])
{
    // Variables to store the final output
    int maxSum = INT_MIN, finalLeft, finalRight, finalTop, finalBottom;
    int left, right, i;
    int temp[ROW], sum, start, finish;
    // Set the left column
    for (left = 0; left < COL; ++left)
    {
        // Initialize all elements of temp as 0
        memset(temp, 0, sizeof(temp));
        // Set the right column for the left column set by outer loop
        for (right = left; right < COL; ++right)
        {
            // Calculate sum between current left and right for every row 'i'
            for (i = 0; i < ROW; ++i)
                temp[i] += M[i][right];
            // Find the maximum sum subarray in temp[]. The kadane() function
            // also sets values of start and finish.  So 'sum' is sum of
            // rectangle between (start, left) and (finish, right) which is the
            //  maximum sum with boundary columns strictly as left and right.
            sum = kadane(temp, &start, &finish, ROW);
            // Compare sum with maximum sum so far. If sum is more, then update
            // maxSum and other output values
            if (sum > maxSum)
            {
                maxSum = sum;
                finalLeft = left;
                finalRight = right;
                finalTop = start;
                finalBottom = finish;
            }
        }
    }
    // Print final values
    printf("(Top, Left) (%d, %d)\n", finalTop, finalLeft);
    printf("(Bottom, Right) (%d, %d)\n", finalBottom, finalRight);
    printf("Max sum is: %d\n", maxSum);
}
int kadane(int* arr, int* start, int* finish, int n)
{
    // initialize sum, maxSum and
    int sum = 0, maxSum = INT_MIN, i;
    // Just some initial value to check for all negative values case
    *finish = -1;
    // local variable
    int local_start = 0;
    for (i = 0; i < n; ++i)
    {
        sum += arr[i];
        if (sum < 0)
        {
            sum = 0;
            local_start = i+1;
        }
        else if (sum > maxSum)
        {
            maxSum = sum;
            *start = local_start;
            *finish = i;
        }
    }
     // There is at-least one non-negative number
    if (*finish != -1)
        return maxSum;
    // Special Case: When all numbers in arr[] are negative
    maxSum = arr[0];
    *start = *finish = 0;
    // Find the maximum element in array
    for (i = 1; i < n; i++)
    {
        if (arr[i] > maxSum)
        {
            maxSum = arr[i];
            *start = *finish = i;
        }
    }
    return maxSum;
}
http://prismoskills.appspot.com/lessons/Dynamic_Programming/Chapter_07_-_Submatrix_with_largest_sum.jsp
int findMaxSum (int matrix[numRows][numCols])
{
    int maxSum=0;

    for (int left = 0; left < numCols; left++)
    {
        int temp[numRows] = {0};

        for (int right = left; right < numCols; right++)
        {
            // Find sum of every mini-row between left and right columns and save it into temp[]
            for (int i = 0; i < numRows; ++i)
                temp[i] += matrix[i][right];

            // Find the maximum sum subarray in temp[].
            int sum = kadane(temp, numRows);

            if (sum > maxSum)
                maxSum = sum;
        }
    }

    return maxSum;
}
How the algorithm executes:


http://www.shuatiblog.com/blog/2014/08/01/Max-Sum-In-2D-Array/
O(n3)
Try convert this question to “max sum in 1D array” by sum up all numbers in the same column. (we know that in 1D array, the algo runs O(n) time)
There’s in total O(n2) combinations of ways to sum up a column. So the total time complexity is O(n3).
public int maxSum(int[][] A) {
    int m = A.length;
    int n = A[0].length;
    int maxResult = Integer.MIN_VALUE;
    for (int i = 0; i < m; i++) {
        int[] temp = new int[n];
        for (int j = i; j < m; j++) {
            // from row#i to row#(m-1), add the number into temp[]
            for (int k = 0; k < n; k++) {
                temp[k] += A[j][k];
            }
            // find max sum for 1D array
            maxResult = Math.max(maxResult, maxSum(temp));
        }
    }
    return maxResult;
}

private int maxSum(int[] B) {
    int sumSoFar = 0;
    int maxSum = Integer.MIN_VALUE;
    for (int i = 0; i < B.length; i++) {
        maxSum = Math.max(maxSum, sumSoFar + B[i]);
        sumSoFar = Math.max(0, sumSoFar + B[i]);
    }
    return maxSum;
}

https://tianrunhe.wordpress.com/category/questions-from-careercup/
O(n^4)
Go through each submatrix (O(n^4) of them) and compute their sums, keeping track of the curren maximum. Without additional optimization, the computation of sums cost O(n^4), giving total a O(n^6) algorithm. We can precompute some sums to reduce the total costs to O(n^4):
Let’s say we need to compute the sum of the submatrix (startRow, startCol) -> (endRow, endCol), S. We can first pre-compute the sum of (0,0) -> (endRow, endCol) denote as S_{1}, the sum of (0,0) -> (endRow, startCol-1) denote as S_{2}, the sum of (0,0) -> (startRow-1, endCol) denote as S_{3} and the sum of (0,0) -> (startRow-1, startCol-1) denote as S_{4}, then S = S_{1} - S{2} - S{3} + S{4}. Hence, we can pre-compute sums of (0,0) -> (i,j) for every pair of (i,j). Then we searching, the computation of sum of submatrix will only need a single operation.
private static int[][] sums;
private static void preComputeSums(int[][] matrix) {
    sums = new int[matrix.length][matrix[0].length];
    for (int i = 0; i < matrix.length; ++i) {
        for (int j = 0; j < matrix[i].length; ++j) {
            if (i == 0 && j == 0)
                sums[i][j] = matrix[i][j];
            else if (j == 0)
                sums[i][j] = sums[i - 1][j] + matrix[i][j];
            else if (i == 0)
                sums[i][j] = sums[i][j - 1] + matrix[i][j];
            else
                sums[i][j] = sums[i - 1][j] + sums[i][j - 1]
                        - sums[i - 1][j - 1] + matrix[i][j];
        }
    }
}
private static int computeSum(int[][] matrix, int startRow, int endRow,
        int startCol, int endCol) {
    if (startRow == 0 && startCol == 0)
        return sums[endRow][endCol];
    else if (startRow == 0)
        return sums[endRow][endCol] - sums[endRow][startCol - 1];
    else if (startCol == 0)
        return sums[endRow][endCol] - sums[startRow - 1][endCol];
    else
        return sums[endRow][endCol] - sums[endRow][startCol - 1]
                - sums[startRow - 1][endCol]
                + sums[startRow - 1][startCol - 1];
}
public static int[][] findSubmatrixWithMaxSum(int[][] matrix) {
    preComputeSums(matrix);
    int startRow = 0, endRow = 0;
    int startCol = 0, endCol = 0;
    int maxSum = Integer.MIN_VALUE;
    for (int i = 0; i < matrix.length; ++i) {
        for (int j = 0; j < matrix[i].length; ++j) {
            for (int rowPtr = i; rowPtr < matrix.length; ++rowPtr) {
                for (int colPtr = j;
                    colPtr < matrix[rowPtr].length; ++colPtr) {
                    int sum = computeSum(matrix, i, rowPtr, j, colPtr);
                    if (sum > maxSum) {
                        maxSum = sum;
                        startRow = i;
                        endRow = rowPtr;
                        startCol = j;
                        endCol = colPtr;
                    }
                }
            }
        }
    }
    int[][] subMatrix = new int[endRow - startRow + 1][endCol - startCol
            + 1];
    for (int i = startRow; i <= endRow; ++i)
        System.arraycopy(matrix[i], startCol, subMatrix[i - startRow], 0,
                endCol - startCol + 1);
    return subMatrix;
}
https://github.com/wzhishen/cracking-the-coding-interview/blob/master/src/chap18/Q12.java
//brute force: O(n^6) time. //returns [r1, c1, r2, c2, sum] where (r1,c1),(r2,c2) represents //the diagonal of submatrix
static int[] findLargestSubmatrix(int[][] matrix) {
int maxSum = Integer.MIN_VALUE;
int[] ret = {-1, -1, -1, -1, 0};
for (int r1 = 0; r1 < matrix.length; ++r1) {
for (int r2 = r1; r2 < matrix.length; ++r2) {
for (int c1 = 0; c1 < matrix[0].length; ++c1) {
for (int c2 = c1; c2 < matrix[0].length; ++c2) {
int sum = getSum(matrix, r1, c1, r2, c2);
if (sum > maxSum) {
maxSum = sum;
ret = new int[] {r1, c1, r2, c2, sum};
}
}
}
}
}
return ret;
}


Code http://analgorithmist.blogspot.com/2012/10/applying-kadanes-algorithm-to-2-d-array.html
http://alexeigor.wikidot.com/kadane
Maximum Rectangle | N00tc0d3r
Also check http://ihaventyetdecided.blogspot.com/2010/10/kadanes-2d-algorithm.html
http://massivealgorithms.blogspot.com/2014/08/find-max-sum-in-2d-array-programming.html
Read full article from Dynamic Programming | Set 27 (Maximum sum rectangle in a 2D matrix)

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts