1. 程式人生 > >[LeetCode] Max Sum of Rectangle No Larger Than K 最大矩陣和不超過K

[LeetCode] Max Sum of Rectangle No Larger Than K 最大矩陣和不超過K

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:

Given matrix = [
  [1,  0, 1],
  [0, -2, 3]
]
k = 2

The answer is 2. Because the sum of rectangle [[0, 1], [-2, 3]] is 2 and 2 is the max number no larger than k (k = 2).

Note:

  1. The rectangle inside the matrix must have an area > 0.
  2. What if the number of rows is much larger than the number of columns?

Credits:
Special thanks to @fujiaozhu for adding this problem and creating all test cases.

這道題給了我們一個二維陣列,讓我們求和不超過的K的最大子矩形,那麼我們首先可以考慮使用brute force來解,就是遍歷所有的子矩形,然後計算其和跟K比較,找出不超過K的最大值即可。就算是暴力搜尋,我們也可以使用優化的演算法,比如建立累加和,參見之前那道題

Range Sum Query 2D - Immutable,我們可以快速求出任何一個區間和,那麼下面的方法就是這樣的,當遍歷到(i, j)時,我們計算sum(i, j),表示矩形(0, 0)到(i, j)的和,然後我們遍歷這個矩形中所有的子矩形,計算其和跟K相比,這樣既可遍歷到原矩形的所有子矩形,參見程式碼如下:

解法一:

class Solution {
public:
    int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
        if (matrix.empty() || matrix[0
].empty()) return 0; int m = matrix.size(), n = matrix[0].size(), res = INT_MIN; int sum[m][n]; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int t = matrix[i][j]; if (i > 0) t += sum[i - 1][j]; if (j > 0) t += sum[i][j - 1]; if (i > 0 && j > 0) t -= sum[i - 1][j - 1]; sum[i][j] = t; for (int r = 0; r <= i; ++r) { for (int c = 0; c <= j; ++c) { int d = sum[i][j]; if (r > 0) d -= sum[r - 1][j]; if (c > 0) d -= sum[i][c - 1]; if (r > 0 && c > 0) d += sum[r - 1][c - 1]; if (d <= k) res = max(res, d); } } } } return res; } };

下面這個演算法進一步的優化了執行時間,這個演算法是基於計算二維陣列中最大子矩陣和的演算法,可以參見youtube上的這個視訊Maximum Sum Rectangular Submatrix in Matrix dynamic programming/2D kadane。這個演算法巧妙在把二維陣列按行或列拆成多個一維陣列,然後利用一維陣列的累加和來找符合要求的數字,這裡用了lower_bound來加快我們的搜尋速度,也可以使用二分搜尋法來替代。我們建立一個集合set,然後開始先放個0進去,為啥要放0呢,因為我們要找lower_bound(curSum - k),當curSum和k相等時,0就可以被返回了,這樣我們就能更新結果了。由於我們對於一維陣列建立了累積和,那麼sum[i,j] = sum[i] - sum[j],其中sums[i,j]就是目標子陣列需要其和小於等於k,然後sums[j]是curSum,而sum[i]就是我們要找值,當我們使用二分搜尋法找sum[i]時,sum[i]的和需要>=sum[j] - k,所以也可以使用lower_bound來找,參見程式碼如下:

解法二:

class Solution {
public:
    int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
        if (matrix.empty() || matrix[0].empty()) return 0;
        int m = matrix.size(), n = matrix[0].size(), res = INT_MIN;
        for (int i = 0; i < n; ++i) {
            vector<int> sum(m, 0);
            for (int j = i; j < n; ++j) {
                for (int k = 0; k < m; ++k) {
                    sum[k] += matrix[k][j];
                }
                int curSum = 0, curMax = INT_MIN;
                set<int> s;
                s.insert(0);
                for (auto a : sum) {
                    curSum += a;
                    auto it = s.lower_bound(curSum - k);
                    if (it != s.end()) curMax = max(curMax, curSum - *it);
                    s.insert(curSum);
                }
                res = max(res, curMax);
            }
        }
        return res;
    }
};

類似題目:

參考資料: