这题是一维最大子段和的扩展。最大子段和的问题在编程珍珠这本书上讲的很透彻,最优算法的复杂度只要O(N),基本思想就是边扫描数据边累加,如果累加的和小于0,则重置累加和为0并继续扫描后面的数,最后累加和最大的那个就是最大子段和。
对于本题矩阵情况,可以将其转换为一维来计算,方法就是对任意连续几行进行压缩。比如对矩阵a的第i行到第j行的压缩就是计算第i行到第j行的每列的和,这样就转换成只有1行列和了,这样就可以用一维的方法进行计算了。
例如
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
取i=0,j=2,则表示0~1行的子矩阵,用colsum[k][j]表示第k列的前j行(0,1...,j-1行)的列和。
则0~1行的子矩阵转换成列和的方法就是colsum[k][j]-colsum[k][i]。结果为9,0,-13,2.然后对该列和求最大子段和。通过遍历所有可能的连续行子矩阵,最后得到最大的那个即为最大子矩阵和。由于遍历需要O(N^2)时间,因此该算法总的复杂度为O(N^3)
代码如下:
Problem: 1050 User: absolute
Memory: 280K Time: 16MS
Language: C++ Result: Accepted
#include <stdio.h>
int main(int argc, char* argv[])
{
int n,i,j,k;
scanf("%d",&n);
int a[100][100];
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
scanf("%d",&a[i][j]);
}
//colsum[i][j]表示第i列前j行的和
int colsum[101][101]={0};
int maxsub = 0;
for(i=0;i<n;i++)
{
for(j=i+1;j<n+1;j++)
{
//计算第i行到第j行(不含第j行)的子矩阵的最大列子段和
int curmax=0,sum=0;
for(k=0;k<n;k++)
{
colsum[k][j] = colsum[k][j-1]+a[k][j-1];
sum+=colsum[k][j]-colsum[k][i];
if(sum<0)
sum=0;
if(sum>curmax)
curmax = sum;
}
if(curmax>maxsub)
maxsub=curmax;
}
}
printf("%d\n",maxsub);
return 0;
}