题意
给一个矩阵,求矩阵内最大的和,并且该和不超过k。
思路
我们先考虑求最大子矩形和。
时间复杂度是
O(n3)
的。其基本思路就是降维。我们枚举L和R,分别代表起始列和终止列的起点和终点,这是
O(n2)
的,然后降维后,转化成了一个一维数组,求一维数组的最大区间和即可。时间复杂度
O(n)
。所以求最大子矩阵的面积是
O(n3)
的。
然后对于这道题,我们可以用相同的思路去做,但是转化成了一个一维数组后,我们需要做的就是求最大子区间的和,并且子区间的和不超过k。
假设我们有一个大小为n的一维数组,其基本思想是维护前缀和+二分查找。
我们假设到当前点的前缀和为 si ,我们不能超过的和为k,那么我们去前缀和里面二分查找一下最小的x,并且 x≥si−k 的值。这里注意有两个点:
- 如果数组的数全部为正数,我们的前缀和本身就是排好序的,如果是负数的话,那么我们二分查找利用的性质是排好序,所以我们用一个set来维护前缀和。因为set的插入时间复杂度是 O(logn) ,所以我们求每一行的第k大的时间复杂度近似于 O(n(logn)2) 、
- 我们在二分查找的时候,最小会返回set里面最小的那个元素,即如果我们本身需要的
x≥−2
即可,假设
set
里面最小的为2,那么返回的结果实际上是 si−2 。这时候是实际上需要的就 si 即可。所以我们每次在set
里面插入0,保证只取 si 的情况能够被考虑到。
时间复杂度: O(m2n(logn)2)
代码
class Solution {
public:
int maxSumSubmatrix(vector<vector<int>>& a, int k) {
int m = a.size(); bool flag = false; int ans = INT_MIN;
if (m) {
//m^2nlogn(so need to swap m, n)
int n = a[0].size();
//use preh to maintain the 1D array
if (m > n) {swap(m, n); flag = true;}
for (int l = 0; l < m; l++) {
vector<int> preh(n, 0);
for (int r = l; r < m; r++) {
int sum = 0;
//pres.insert(0) to make sure that we can get the pres[j], otherwise, it's always pres[j] - pres[pos]
set<int> pres; pres.insert(0);
for (int j = 0; j < n; j++) {
preh[j] += flag ? a[j][r] : a[r][j];
sum += preh[j];
int x = sum - k;
auto it = pres.lower_bound(x);
if (it != pres.end()) ans = max(ans, sum - *it);
pres.insert(sum);
}
}
}
return ans;
}
return 0;
}
};