如果暴力枚举的话,确定矩形区域需要两个顶点,时间复杂度就是,m和n最大100,计算量最大为,容易超时。
确定一个矩形区域一定要四条边,但我们要确定一个矩形区域是想求出它的和以满足最大的不超过k。而矩形区域的和可以看成是一列一列的和,如果我们把每一列的和全部求出来,实际上矩形区域的和就是一个数组的和。
我们可以只用枚举三条边,而第四条边可以采用二分查找,减少第四条边的枚举。
对于一维数组,想要知道区间内的和可以使用前缀和,也就是数组第i项的值为前i项的和 ,其实在求和的过程中就确定了第三条边。
for (int i = 0; i < m; ++i) { // 枚举上边界
for (int j = i; j < m; ++j) { // 枚举下边界
for (int c = 0; c < n; ++c) {
sum[c] += matrix[j][c]; // 更新每列的元素和
}
}
}
既然第四条边需要使用到二分查找,我们可以用set里的lower_bound来求。 假定第四条边对应着前i项的值,第三条边对应着前j项的值,我们就是要找出满足的最大,变一下式子,就是要找出满足的最小,这个可以用set来维护。
auto lb = sumSet.lower_bound(s - k);
if (lb != sumSet.end()) {
ans = max(ans, s - *lb);
}
sumSet.insert(s);
完整代码C++
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>> &matrix, int k) {
int ans = INT_MIN;
int m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; ++i) { // 枚举上边界
vector<int> sum(n);
for (int j = i; j < m; ++j) { // 枚举下边界
for (int c = 0; c < n; ++c) {
sum[c] += matrix[j][c]; // 更新每列的元素和
}
set<int> sumSet{0};
int s = 0;
for (int v : sum) {
s += v;
auto lb = sumSet.lower_bound(s - k);
if (lb != sumSet.end()) {
ans = max(ans, s - *lb);
}
sumSet.insert(s);
}
}
}
return ans;
}
};
补充关于set的用法。
insert()//插入元素
count()//判断容器中是否存在某个元素
size()//返回容器的尺寸,也可以元素的个数
erase()//删除集合中某个元素
clear()//清空集合
empty()//判断是否为空
begin()//返回第一个节点的迭代器
end()//返回最后一个节点加1的迭代器
rbegin()//反向迭代器
rend()//反向迭代器
find()//查找某个指定元素的迭代器
lower_bound()//二分查找第一个不小于某个值的元素的迭代器
get_allocator()//返回集合的分配器
swap()//交换两个集合的变量
max_size()//返回集合能容纳元素的最大限值