枚举技巧——积分图
很多时候,我们会处理矩阵上问题,大部分都是寻找一个可行最优子矩形区域。如果枚举两个顶点时间复杂度为 O ( ( N M ) 2 ) O((NM)^2) O((NM)2)。下面介绍一种积分图的方式。
积分图
我们枚举上下两个边界,将边界围成的矩形压成一个数组,至于同列的数据怎么合并,还要看题目的具体要求。
之后,我们面对的就是一个一维问题了,相对来说,同等的一维问题更好解决,一般是使用DP求解。
这样一来,时间复杂度就是 O ( N 2 M ) O(N^2M) O(N2M)。时间复杂度降低了一个等级。
如果列数比较少,行数比较多,我们还可以合并列。
例题
class Solution
{
public:
int psum[105][105];
void buildPsum(vector<vector<int>> &matrix)
{
for (int r = 1; r <= matrix.size(); r++)
for (int c = 1; c <= matrix[0].size(); c++)
{
psum[r][c] = psum[r][c - 1] + psum[r - 1][c] - psum[r - 1][c - 1] + matrix[r - 1][c - 1];
}
}
int getSum(int r1, int r2, int c)
{
return psum[r2 + 1][c + 1] - psum[r1][c + 1] - psum[r2 + 1][c] + psum[r1][c];
}
int solve(int r1, int r2, int cm, int k)
{
set<int> st;
int ppsum = 0;
int ans = INT_MIN;
st.insert(0);
for (int i = 0; i < cm; i++)
{
ppsum += getSum(r1, r2, i);
auto it = st.lower_bound(ppsum - k);
if (it != st.end())
{
ans = max(ans, ppsum - *it);
}
st.insert(ppsum);
}
return ans;
}
int maxSumSubmatrix(vector<vector<int>> &matrix, int k)
{
buildPsum(matrix);
// 枚举行
int ans = INT_MIN;
for (int r1 = 0; r1 < matrix.size(); r1++)
{
for (int r2 = r1; r2 < matrix.size(); r2++)
{
ans = max(ans, solve(r1, r2, matrix[0].size(), k));
}
}
return ans;
}
};