Skip to content

Latest commit

 

History

History

363

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Given an m x n matrix matrix and an integer k, return the max sum of a rectangle in the matrix such that its sum is no larger than k.

It is guaranteed that there will be a rectangle with a sum no larger than k.

 

Example 1:

Input: matrix = [[1,0,1],[0,-2,3]], k = 2
Output: 2
Explanation: Because the sum of the blue rectangle [[0, 1], [-2, 3]] is 2, and 2 is the max number no larger than k (k = 2).

Example 2:

Input: matrix = [[2,2,-1]], k = 3
Output: 3

 

Constraints:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 100
  • -100 <= matrix[i][j] <= 100
  • -105 <= k <= 105

 

Follow up: What if the number of rows is much larger than the number of columns?

Companies:
Roblox, Google, Apple

Related Topics:
Array, Binary Search, Dynamic Programming, Matrix, Ordered Set

Note

The brute force solution is enumerating O((MN)^2) all rectangles and for each rectangle sum the matrix sum in O(MN) time. The overall time complexity is O((MN)^3) which is too slow.

A better brute force is using prefix sum to save the O(MN) time calculating the matrix sum given a rectangle. The overall time complexity is O((MN)^2). Since M and N are at most 100, the time complexity is around O(10^8) which will still get TLE.

Now consider a single row matrix, we can scan the matrix from left to right and keep storing the prefix sums we've seen in a set. For the current prefix sum S, we want to find a prefix sum X from set that S - X <= K, i.e. X >= S - K. To find such a X, we can leverage lower_bound of set which takes O(logN) time. So this algorithm overall takes O(NlogN) time.

For a 2 x N matrix, how to find a 2-row submatrix with sum <= K? We can compress the matrix by summing each column individually.

2  1 -4
1  0  1
// after compression
3  1 -3 

Then for this compressed (3 1 -3) vector, we can use the algorithm above for a single row matrix.

For a M x N matrix, we can enumerate different starting rows. For each starting row, we extend the bottom edge of the matrix continuously until it reaches the end. During this extention, we can keep compressing the matrix and running the algorithm for the compressed vector.

The overall time complexity is `O(M^2 * NlogN)

Solution 1. DP + Binary Search

// OJ: https://leetcode.com/problems/max-sum-of-rectangle-no-larger-than-k/
// Author: github.com/lzl124631x
// Time: O(M^2 * NlogN)
// Space: O(N)
class Solution {
public:
    int maxSumSubmatrix(vector<vector<int>>& A, int k) {
        int M = A.size(), N = A[0].size(), ans = INT_MIN;
        for (int i = 0; i < M; ++i) { // starting row
            vector<int> prefix(N, 0);
            for (int j = i; j < M; ++j) { // ending row
                int sum = 0;
                set<int> s{0};
                for (int t = 0; t < N; ++t) {
                    sum += A[j][t];
                    prefix[t] += sum;
                    int target = prefix[t + 1] - k;
                    auto it = s.lower_bound(target);
                    if (it != s.end()) {
                        ans = max(ans, prefix[t] - *it);
                    }
                    s.insert(prefix[t]);
                }
            }
        }
        return ans;
    }
};

Or extract the maxSumSubarray function.

// OJ: https://leetcode.com/problems/max-sum-of-rectangle-no-larger-than-k/
// Author: github.com/lzl124631x
// Time: O(M^2 * NlogN)
// Space: O(N)
class Solution {
    int maxSumSubarray(vector<int> &A, int k) {
        set<int> s{0};
        int ans = INT_MIN;
        for (int n : A) {
            int target = n - k;
            auto it = s.lower_bound(target);
            if (it != s.end()) ans = max(ans, n - *it);
            s.insert(n);
        }
        return ans;
    }
public:
    int maxSumSubmatrix(vector<vector<int>>& A, int k) {
        int M = A.size(), N = A[0].size(), ans = INT_MIN;
        for (int i = 0; i < M; ++i) { // starting row
            vector<int> prefix(N, 0);
            for (int j = i; j < M; ++j) { // ending row
                int sum = 0;
                for (int t = 0; t < N; ++t) {
                    sum += A[j][t];
                    prefix[t] += sum;
                }
                ans = max(ans, maxSumSubarray(prefix, k));
            }
        }
        return ans;
    }
};

Solution 2. Transpose Matrix to Reduce Time

Considering the follow-up, the original algorithm is using O(M^2 * NlogN) time and M is always the number of rows.

If the number of rows is way larger than number of columns, we should transpose the matrix.

// OJ: https://leetcode.com/problems/max-sum-of-rectangle-no-larger-than-k/
// Author: github.com/lzl124631x
// Time: O(min(M, N)^2 * max(M, N)log(max(M, N)))
// Space: O(max(M, N))
class Solution {
    int maxSumSubarray(vector<int> &A, int k) {
        set<int> s{0};
        int ans = INT_MIN;
        for (int n : A) {
            int target = n - k;
            auto it = s.lower_bound(target);
            if (it != s.end()) ans = max(ans, n - *it);
            s.insert(n);
        }
        return ans;
    }
public:
    int maxSumSubmatrix(vector<vector<int>>& A, int k) {
        int M = A.size(), N = A[0].size(), ans = INT_MIN;
        bool swapped = M > N;
        if (swapped) swap(M, N);
        for (int i = 0; i < M; ++i) { // starting row
            vector<int> prefix(N, 0);
            for (int j = i; j < M; ++j) { // ending row
                int sum = 0;
                for (int t = 0; t < N; ++t) {
                    sum += swapped ? A[t][j] : A[j][t];
                    prefix[t] += sum;
                }
                ans = max(ans, maxSumSubarray(prefix, k));
            }
        }
        return ans;
    }
};

Solution 3. With Kadane Optimization

The maxSumSubarray function takes O(NlogN) time. We can use Kadane algorithm to get the maximum subarray sum in O(N) time, and if the maximum subarray sum is no more than k, we can directly return it.

// OJ: https://leetcode.com/problems/max-sum-of-rectangle-no-larger-than-k/
// Author: github.com/lzl124631x
// Time: O(min(M, N)^2 * max(M, N)log(max(M, N)))
// Space: O(max(M, N))
class Solution {
    int kadane(vector<int> &A) {
        int ans = INT_MIN, mx = 0;
        for (int n : A) {
            mx = max(mx + n, n);
            ans = max(ans, mx);
        }
        return ans;
    }
    int maxSumSubarray(vector<int> &A, int k) {
        int maxKadane = kadane(A);
        if (maxKadane <= k) return maxKadane;
        set<int> s{0};
        int ans = INT_MIN, sum = 0;
        for (int n : A) {
            sum += n;
            int target = sum - k;
            auto it = s.lower_bound(target);
            if (it != s.end()) ans = max(ans, sum - *it);
            s.insert(sum);
        }
        return ans;
    }
public:
    int maxSumSubmatrix(vector<vector<int>>& A, int k) {
        int M = A.size(), N = A[0].size(), ans = INT_MIN;
        bool swapped = M > N;
        if (swapped) swap(M, N);
        for (int i = 0; i < M; ++i) { // starting row
            vector<int> sum(N, 0);
            for (int j = i; j < M; ++j) { // ending row
                for (int t = 0; t < N; ++t) sum[t] += swapped ? A[t][j] : A[j][t];
                ans = max(ans, maxSumSubarray(sum, k));
            }
        }
        return ans;
    }
};

Reference