题目
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:
The rectangle inside the matrix must have an area > 0.
What if the number of rows is much larger than the number of columns?
解题思路:
最先想到的是暴力解法,把所有可能的矩阵都算一遍然后找出比k小的最大的,这样时间复杂度是O(n4),效率是很低的,因为有许多重复计算,要优化算法就要减少重复计算。可以一列列推过去,用一个sum记录每一行的起始列到当前列之和,然后再把每行遍历一遍,从而减少重复计算次数。这样遍历的矩阵的左上角都是在第一行,中间的矩阵的和可以通过相减获得,第i行到第j行的和 = 第j行的和 - 第i行的和。
又由于题目有限制矩阵之和要小于k,所以可以使用一个集合,把每一行之和都放进去,然后每得到新的和s,都在集合里用lower_bound函数找出在s-k右边的最小的数,然后用s减去这个数,就是当前行可以找的一个小于k的矩阵和,这里还有一个小细节就是要把0放进集合里面,这样就可以使得当s刚好为k的时候,可以返回k。该算法的算法时间复杂度为O(n3logn),这样说可能不太清楚,具体请看如下代码:(可AC)
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
int row = matrix.size(), col = matrix[0].size();
int res = INT_MIN;
for(int i=0; i<col; ++i){ //第i列开始
vector<int> sum(row,0);
for(int j=i; j<col; ++j){ //第j列结束
for(int h=0; h<row; ++h)
sum[h] += matrix[h][j]; //每一行累加的和,减少重复计算
set<int> sums;
int s=0,maxS=INT_MIN;
sums.insert(0);
for(int h=0; h<row; ++h){
s += sum[h];
//获取插入s而不破坏set顺序的最小位置的指针
set<int>::iterator it = sums.lower_bound(s-k);
if(it!=sums.end()) maxS = max(s-*it,maxS);
if(maxS == k) return k;
sums.insert(s);
}
res = max(res,maxS);
}
}
return res;
}
};
额外思考:
如果题目没有要求矩阵和小于k,而是只要求最大的,则可以有O(n3)的算法,代码如下(没有k限制版)
int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
int row = matrix.size(), col = matrix[0].size();
int res = INT_MIN;
for(int i=0; i<col; ++i){ //第i列开始
vector<int> sum(row,0);
for(int j=i; j<col; ++j){ //第j列结束
for(int h=0; h<row; ++h)
sum[h] += matrix[h][j]; //每一行累加的和,减少重复计算
int s = 0;
for(int h=0; h<row;++h){
s += sum[h];
if(s==k) return k;
if(s>res && s<k) res = s;
if(s<0) s = 0;
}
}
}
return res;
}