Search code examples
algorithmmaxdynamic-programmingsubmatrix

Maximum subarray of size HxW within a 2D matrix


Given a 2-dimensional array of positive integers, find the subrectangle of size HxW with the largest sum. The sum of a rectangle is the sum of all the elements in that rectangle.

Input: A 2D array NxN with positive elements The HxW size of the subrectangle

Output: The submatrix of HxW size with the largest sum of its elements.

I've solved this using a brute-force method, however, I'm now looking for a better solution with better complexity (my brute-force method's complexity is O(n6)).


Solution

  • First create the cumulative sum of your matrix: O(n2)

    Matrix
    2 4 5 6
    2 3 1 4
    2 0 2 1
    
    Cumulative sum
    2 6  11 17
    4 11 17 27
    6 13 21 32
    

    cumulative_sum(i,j) is the sum of all the elements in the submatrix (0:i,0:j). You can calculate the cumulative sum matrix using below logic:

    cumulative_sum(i,j) = cumulative_sum(i-1,j) + cumulative_sum(i,j-1) - cumulative_sum(i-1,j-1) + matrix(i,j)
    

    Using the cumulative sum matrix you can calculate sum of every sub-matrix in O(1):

    calculating sum of submatrix (r1 ... r2 , c1 ... c2)
    sum_sub = cumulative_sum(r2,c2) - cumulative_sum(r1-1,c2) - cumulative_sum(r2,c1-1) + cumulative_sum(r1-1,c1-1)
    

    Inclusion-Exclusion

    Then using two loops you can put the top-left of your HW rectangle on every point of the matrix and calculate the sum of that rectangle.

    for r1=0->n_rows
       for c1=0->n_cols
           r2 = r1 + height - 1
           c2 = c1 + width - 1
           if valid(r1,c1,r2,c2) // doesn't exceed the original matrix
                sum_sub = ... // formula mentioned above
                best = max(sum_sub, best)
    return best
    

    This approach is in O(N2).

    Here is the python implementation:

    from copy import deepcopy
    
    def findMaxSubmatrix(matrix, height, width):
        nrows = len(matrix)
        ncols = len(matrix[0])           
    
        cumulative_sum = deepcopy(matrix)
    
        for r in range(nrows):
            for c in range(ncols):
                if r == 0 and c == 0:
                    cumulative_sum[r][c] = matrix[r][c]
                elif r == 0:
                    cumulative_sum[r][c] = cumulative_sum[r][c-1] + matrix[r][c]
                elif c == 0:
                    cumulative_sum[r][c] = cumulative_sum[r-1][c] + matrix[r][c]
                else:
                    cumulative_sum[r][c] = cumulative_sum[r-1][c] + cumulative_sum[r][c-1] - cumulative_sum[r-1][c-1] + matrix[r][c]
    
        best = 0
        best_pos = None
    
        for r1 in range(nrows):
            for c1 in range(ncols):
                r2 = r1 + height - 1
                c2 = c1 + width - 1
                if r2 >= nrows or c2 >= ncols:
                    continue
                if r1 == 0 and c1 == 0:
                    sub_sum = cumulative_sum[r2][c2]
                elif r1 == 0:
                    sub_sum = cumulative_sum[r2][c2] - cumulative_sum[r2][c1-1]
                elif c1 == 0:
                    sub_sum = cumulative_sum[r2][c2] - cumulative_sum[r1-1][c2]
                else:
                    sub_sum = cumulative_sum[r2][c2] - cumulative_sum[r1-1][c2] - cumulative_sum[r2][c1-1] + cumulative_sum[r1-1][c1-1]
                if best < sub_sum:
                    best_pos = r1,c1
                    best = sub_sum
    
        print "maximum sum is:", best
        print "top left corner on:", best_pos
    
    
    matrix = [ [2,4,5,6],
               [2,3,1,4],
               [2,0,2,1] ]
    findMaxSubmatrix(matrix,2,2)
    

    output

    maximum sum is: 16
    top left corner on: (0, 2)