最大子矩阵
题目描述
给定一个整数二维数组。子矩形是在整个阵列中,一个连续的大小为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×N的整数数组。数组中的数字将在区间[- 127,127 ]
输出描述
输出最大子矩形之和。
样例输入
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
样例输出
15
数据范围及描述
N <=100
首先我们需要了解最大子段和。
举一个例子,有7个数如
1 -3 4 -2 8 -7 8
那么可以轻松地求最大字段和。
如果sum一直加到小于0,那么说明前面的字段都毫无意义,否则就继续往后加,中途每次都比较最大值。
下面我们简单模拟一下,设一个当前记录和的变量sum
sum+=a[2] -> sum=1-3=-2 max=1
sum+=a[3] -> 因为sum<0,所以sum=0+4=4 max=4
sum+=a[4] -> sum=4-2=2 max=4
sum+=a[5] -> sum=2+8=10 max=10
sum+=a[6] -> sum=10-7=3 max=10
sum+=a[7] -> sum=3+8=11 max=11
这样就很清楚的求出了最大字段和,下面是基础程序
int maxsub(int a[],int n)
{
int i,max=0,sum=0;
for(i=0;i<n;i++)
{
if(sum>0) sum+=a[i];
else sum=a[i];
if(sum>max) max=sum;
}
return max;
}
那么这里是二维的一个矩阵,我们可以把他压缩成一维的就可以做最大字段和了。
怎么压缩成一维的呢,就是把n行变成1行,然后就变成了n个数的数列。
那么首先需要确定行的起点,然后需要确定行的终点,把同一列的从起始行到终止行都加起来,变成一个数,然后再求最大字段和,说明这是一个n^3的算法。
举一个例子
1 -2 3
-2 4 -1
3 -2 0
设一个数组f[n]
则f[1-3]的变化如下
1. 1 -2 3
2. (1-2)(-2+4)(3-1) -1 2 2
3. (1-2+3)(-2+4-2)(3-1+0) -2 0 2
(等于(-1+3)(2-2)(2+0))
4. -2 4 -1
5. (-2+3)(4-2)(-1+0) 1 2 -1
6. -3 -2 0
六种情况不重不漏,分别求最大字段和就行了。
for(int i=1;i<=n;i++){ 起点
将f[]清空为0
for(int j=i;j<=n;j++){ 从起点开始下拉
for(int k=1;k<=n;k++)
f[k]+=a[j][k]; f[k]为从a[i][k]到a[j][k]的和
然后求一个f数组的最大子段和
}
}
就这样很简单的完成了问题
三维的题目也是这样,压缩一个面。
所以下面是我的程序
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cmath>
using namespace std;
int n,a[105][105],f[105];
const int inf=-2147483647;
int max(int a,int b)
{return (a>b?a:b);}
int main(){
freopen("test2.in","r",stdin);
freopen("test2.out","w",stdout);
int ans=inf;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
f[j]=0;
for(int j=i;j<=n;j++){
for(int k=1;k<=n;k++)
f[k]+=a[j][k];
int mx=inf,sum=0;
for(int k=1;k<=n;k++)
{sum=(sum>0?sum+f[k]:f[k]);
mx=max(mx,sum);}
ans=max(mx,ans);
}
}
cout<<ans<<endl;
return 0;
}