Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix such that its sum is no larger than k.
Example:
Input: matrix = [[1,0,1],[0,-2,3]], k = 2
Output: 2
Explanation: Because the sum of rectangle [[0, 1], [-2, 3]]
is 2,
and 2 is the max number no larger than k (k = 2).
Note:
- The rectangle inside the matrix must have an area > 0.
- What if the number of rows is much larger than the number of columns?
The naive solution would be for each sub-matrix of the matrix, we calculate the sum. We compare with k and get a largest one. Since we can do a presum of the matrix before, so getting the sum of the sub-matrix is O(1) operation. The total time complexity of the solution would be O(m * n * m * n).
A better solution:
Think about the 1D problem: If given a array of nums and target k, how can we get the max sum of the subarray of which its sum is no greater than k?
The solution is do a presum of the array first, and for each number sum[i], our goal is to find out a sum[j], where sum[i] - sum[j] <= k, In other words,
sum[j] >= sum[i] - k.
Since our goal is to find out the sum cloest to k, here sum[j] should be the smallest number in sum and it's greater or equal to sum[i] - k.
We can use a treeset to store the previous sum[i]. Treeset provides a convenient function called ceiling(val) which returns the smallest number greater or equal to val in the treeSet. So our target is to find out the ceiling of (sum[i] - k).
Overall, the time complexity of the 1D array is O(n * logn).
Then let's go back to the 2-D matrix problem. We can choose any two of the columns, c1 and c2. And do a presum between c1 and c2. Then the 2D problem can be reduced to 1D.
The overall time complexity is O(n^2 * m * logm). If n >> m, we can do a row-order presum first, find out two rows r1 and r2, do a presum between r1 and r2.
class Solution { public int maxSumSubmatrix(int[][] matrix, int k) { if (matrix == null || matrix.length == 0) { return 0; } int m = matrix.length; int n = matrix[0].length; int[][] preSum = new int[m][n + 1]; for (int i = 0; i < m; i++) { for (int j = 1; j <= n; j++) { preSum[i][j] = preSum[i][j - 1] + matrix[i][j - 1]; } } int ans = Integer.MIN_VALUE; for (int c1 = 1; c1 <= n; c1++) { for (int c2 = c1; c2 <= n; c2++) { int[] preSumRow = new int[m]; for (int row = 0; row < m; row++) { preSumRow[row] = preSum[row][c2] - preSum[row][c1 - 1]; } ans = Math.max(ans, getMaxSubarrayHelper(preSumRow, k)); } } return ans; } private int getMaxSubarrayHelper(int[] nums, int target) { TreeSet<Integer> treeSet = new TreeSet<>(); int ans = Integer.MIN_VALUE; int[] preSum = new int[nums.length + 1]; for (int i = 1; i <= nums.length; i++) { preSum[i] = preSum[i - 1] + nums[i - 1]; } treeSet.add(0); for (int i = 1; i <= nums.length; i++) { Integer num = preSum[i] - target; Integer ceiling = treeSet.ceiling(num); if (ceiling != null) { ans = Math.max(ans, preSum[i] - ceiling); } treeSet.add(preSum[i]); } return ans; } }
No comments:
Post a Comment