给定一个包含整数的二维矩阵,子矩形是位于整个阵列内的任何大小为1 * 1或更大的连续子阵列。
矩形的总和是该矩形中所有元素的总和。
在这个问题中,具有最大和的子矩形被称为最大子矩形。
例如,下列数组:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
其最大子矩形为:
9 2
-4 1
-1 8
它拥有最大和15。
输入格式
输入中将包含一个N*N的整数数组。
第一行只输入一个整数N,表示方形二维数组的大小。
从第二行开始,输入由空格和换行符隔开的N2N2个整数,它们即为二维数组中的N2N2个元素,输入顺序从二维数组的第一行开始向下逐行输入,同一行数据从左向右逐个输入。
数组中的数字会保持在[-127,127]的范围内。
输出格式
输出一个整数,代表最大子矩形的总和。
数据范围
1≤N≤100
输入样例:
4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1
8 0 -2
输出样例:
15
思路:因为要求矩阵所有数的和,所以可以首先处理出纵向的每一竖列的前缀和,将求一个竖列的和的时间复杂度从o(n)优化成o(1),只需要算出a[j][k]-a[i-1][k],就是以第i行为上界,以第j行为下界,第k列这一竖列的和(虽然只有一条竖线,但也可以看成一个矩形,一个矩形就是由这若干条竖线组成的),然后利用贪心的思想,类似dp的处理方法,在矩阵的上下界(i和j确定的矩阵的高)固定的情况下,枚举k从1到n(即矩阵的宽),要求最大矩阵和,每次一个竖列向右扩展时,得到的矩阵的最大和都与上一个状态的最大和有关,这就涉及到决策的最优问题(类似dp最大不相交字段和),就把二维的问题转化成了一维的问题,这里只不过是之前在求dp最大不相交字段和问题中的每一个数都变成了一条线段的和(这条线段的和使用上面前缀和的公式o(1)次的算出的),所以每次决策时,当前最大和=max(上一次的最大和,0)+当前竖列的和(即:上一个最大和>0就累加上,否则当前竖列的和就是当前最大和);
最后总结一下,就是枚举上下界(高度)(o(n^2)),然后枚举所有宽度(o(n)),用dp+前缀和算出当前最大和,这样时间复杂度就是o(n^3).
完整代码:
#include <iostream>
using namespace std;
const int inf=0x3f3f3f3f;
int n,a[110][110];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
a[i][j]+=a[i-1][j];//a[i][j]表示第j列以1为上界i为下界所构成的这一竖列的和
}
}
int ans=-inf;
for(int i=1;i<=n;i++){//枚举上界
for(int j=i;j<=n;j++){//枚举下界
int sum=0;
for(int k=1;k<=n;k++){//枚举所有宽度
sum=max(sum,0)+a[j][k]-a[i-1][k];
ans=max(ans,sum);
}
}
}
cout<<ans<<endl;
return 0;
}