给定一个包含整数的二维矩阵,子矩形是位于整个阵列内的任何大小为 1×11×1 或更大的连续子阵列。
矩形的总和是该矩形中所有元素的总和。
在这个问题中,具有最大和的子矩形被称为最大子矩形。
例如,下列数组:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩形为:
9 2
-4 1
-1 8
它拥有最大和 1515。
输入格式
输入中将包含一个 N×NN×N 的整数数组。
第一行只输入一个整数 NN,表示方形二维数组的大小。
从第二行开始,输入由空格和换行符隔开的 N2N2 个整数,它们即为二维数组中的 N2N2 个元素,输入顺序从二维数组的第一行开始向下逐行输入,同一行数据从左向右逐个输入。
数组中的数字会保持在 [−127,127][−127,127] 的范围内。
输出格式
输出一个整数,代表最大子矩形的总和。
数据范围
1≤N≤1001≤N≤100
输入样例:
4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1
8 0 -2
输出样例:
15
题解
首先,这道题要求的是一个矩形的最大值,我们可能先想到二维和,但是,如果我们直接用二维前缀和暴力枚举,可能会超时,为什么?
这个矩阵式随机的一片区域,用普通二维前缀和,我们就要知道左上角和右下角,求最大的,我们免不了要把每个左上角和右下角列出来,就要用4个嵌套循环,那么时间复杂度是n^4,可能超时
那么,优化的二维前缀和来了。
我们只需要每列的前缀和,也就是g[i][j]+=g[i-1][j];
如何用它来求最大的呢?
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
这个是上下边界,也可理解位每行,第i行和第j行,我们不管它们之间有几行,直接压缩成一个一维,那么每列的前缀和的作用也就有了。
然后就是
for(int k=1;k<=n;k++)
{
last=max(last,0)+g[j][k]-g[i-1][k];
ans=max(ans,last);
}
我们压缩成一维后,这里的k也就第k个数的意思。为什么可以这样做?
a1 a2 a3 a4 a5 a6 a7
我们以a6位结尾的最大和是last
假设a7大于0
如果last<=0,那么以a7为结尾的最大就是a7
相反的,如果last>0,那么以a7为结尾的最大就是last+a7。
#include <iostream>
#include <climits>
#include <algorithm>
using namespace std;
int g[110][110];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
scanf("%d",&g[i][j]);
g[i][j]+=g[i-1][j];
}
//这里的i和j不是几行几列,而是上下边界
int ans=INT_MIN;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
{
int last=0;
for(int k=1;k<=n;k++)
{
last=max(last,0)+g[j][k]-g[i-1][k];
ans=max(ans,last);
}
}
cout<<ans<<endl;
return 0;
}