Largest Sub-Matrix Sum
update Mar 15, 2018 15:56
Given a matrix that contains integers, find the submatrix with the largest sum.
Return the sum of the submatrix.
Assumptions:
The given matrix is not null and has size of M * N, where M >= 1 and N >= 1
Examples
{ {1, -2, -1, 4},
{1, -1, 1, 1},
{0, -1, -1, 1},
{0, 0, 1, 1} }
the largest submatrix sum is (-1) + 4 + 1 + 1 + (-1) + 1 + 1 + 1 = 7.
Basic Idea:
这道题目就是要求一个矩阵中所有子矩阵中元素的最大和。
Naive Solution, brute force
O(n^6)
:如果不用任何技巧,求解这道题我们需要
O(N^6)
(N 为边长)。因为需要四重 for 循环考虑O(N^4)
个所有的submatrix,对于每个 苏北matrix,又需要O(N^2)
的时间求和,一共O(N^6)
;使用一维 prefix sum 数组优化
O(n^5)
:如果我们为每行建一个 prefix sum 数组,我们就可以用
O(N)
的时间求得任意一个 submatrix 的所有元素之和,如此一来时间可以优化为O(N^5)
;使用二维 prefix sum 数组优化
O(n^4)
:试想如果有一个
2D prefix sum matrix : sums = int[][]
,满足sums[i][j] = sum{matrix 从[0,0] 到 [i,j] 的子矩阵}
,则我们可以用O(1)
的时间求得任意 submatrix 的和。而我们可以用O(N^2)
的时间来求得sums 数组,如此一来,就将时间复杂度优化为O(N^4)
如下图所示(但要注意edge case,图中没有考虑):

(Code 见后面)
依然用一维 prefix sum 数组优化,结合 Kadane 求 max subarray sum 的算法
O(n^3)
:与之前的几种方法不同,这次不再遍历每一个 submatrix,而是枚举所有的左右边界对,在每一对左右边界之间,求不同上下边界组成的 submatrix 的最大和,全局最大就是解。
具体地,我们可以先用
O(N^2)
的时间求所有 row 的 prefix sum 数组,如此一来,对于任意给定左右边界的某一行,我们可以用O(1)
时间求和。那么,我们可以把任意左右边界之间的部分视为一个竖着的一维数组,其中的值就是某一行的和。于是,我们就可以使用 Kadane 算法求它的 max subarray sum,该过程耗时O(N)
,而因为共有O(N^2)
个左右边界组合,一共要进行该过程O(N^2)
次,所以总时间复杂度为O(N^3)
。
Java Code:
O(n^4)
解法,2D prefix sum:public class Solution { public int largest(int[][] matrix) { int R = matrix.length, C = matrix[0].length; // 先获取 2D prefix sum, sums[i][j] 表示从 [0,0] 到 [i,j] submatrix 的和 int[][] sums = new int[R][C]; for (int r = 0; r < R; ++r) { for (int c = 0; c < C; ++c) { if (c == 0) { if (r == 0) { // 左上角 sums[r][c] = matrix[r][c]; } else { // 第一行 sums[r][c] = matrix[r][c] + sums[r - 1][c]; } } else { if (r == 0) { // 第一列 sums[r][c] = matrix[r][c] + sums[r][c - 1]; } else { // 右下方普通部分 sums[r][c] = matrix[r][c] + sums[r][c - 1] - sums[r - 1][c - 1] + sums[r - 1][c]; } } } } // 四重 for loop 遍历所有 submatrix int globalMaxSum = Integer.MIN_VALUE; for (int r = 0; r < R; ++r) { for (int c = 0; c < C; ++c) { // 对每个 upper left corner point, 循环遍历其开始的submatrix for (int i = r; i < R; ++i) { for (int j = c; j < C; ++j) { int currMatrixSum = sums[i][j]; if (r > 0) currMatrixSum -= sums[r - 1][j]; if (c > 0) currMatrixSum -= sums[i][c - 1]; if (r > 0 && c > 0) currMatrixSum += sums[r - 1][c - 1]; globalMaxSum = Math.max(globalMaxSum, currMatrixSum); } } } } return globalMaxSum; } }
O(n^3)
解法,prefix sum + Kadane 算法:public class Solution { public int largest(int[][] matrix) { int R = matrix.length, C = matrix[0].length; // 先求每行的 prefix sum int[][] sums = new int[R][C]; for (int r = 0; r < R; ++r) { for (int c = 0; c < C; ++c) { if (c == 0) { sums[r][c] = matrix[r][c]; } else { sums[r][c] = matrix[r][c] + sums[r][c - 1]; } } } // 遍历每一对左右边界,在边界内考虑不同上下边界求最大和submatrix,Kadane 算法 int globalMaxSum = Integer.MIN_VALUE; for (int left = 0; left < C; ++left) { for (int right = left; right < C; ++right) { int currSum = 0; // 当前左右边界间,某上下边界决定的submatrix的sum for (int r = 0; r < R; ++r) { int currRowSum = sums[r][right] - sums[r][left] + matrix[r][left]; if (currSum < 0) { currSum = currRowSum; } else { currSum += currRowSum; } globalMaxSum = Math.max(globalMaxSum, currSum); } } } return globalMaxSum; } }