题意:
给定一个n*n的矩阵,求所有的子矩阵中,输出最大子矩阵和最大是多少。(矩阵和是指矩阵中所有元素相加所得到的值)
题解:
在解这道题之前,我们需要了解最大连续子序列和。给定一个序列,我们求最大连续子序列和的方法是:先令k=0,然后遍历序列,将序列元素加到k上;当k为负数时,k=0(因为使k变为负数的数必定是负数,最大结果肯定不能包含这个元素,假设包含这个元素,那么必须在这个元素之前要有够大的序列和大于这个元素,而根据k的性质是不存在这样的序列的,所以必定不能包含这个元素)。以这种方式到序列最后,那么其中出现的最大的k就是最大连续子序列和。
这道题是先用sum[i][j]=e[1][j]+e[2][j]+..ei][j],那么i行到k行,在j列上的元素和就是f[j]=sum[k][j]-sum[i-1][j];我们枚举子矩阵的高k和开始行i,怎么求宽度?我们得到一个序列f[j]=sum[i+k-1][j]-sum[i-1][j],那么求高度为k,开始行为i的最大子矩阵和就相当于求这个序列f的最大连续子序列和了。那么时间复杂度压缩到了O(n^3)
代码:
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <vector>
#include <cmath>
#include <cstdlib>
using namespace std;
const int maxn=110;
const int INF=1e8;
int e[maxn][maxn];
int sum[maxn][maxn];
int f[maxn],dp[maxn];
int fun(int n)//用dp求f数组中最大连续子串和
{
int i,j,k=0,ans=-INF;
for(i=1;i<=n;i++)
{
k+=f[i];
ans=max(ans,k);
if(k<0)k=0;
}
return ans;
}
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
int i,j,k,ans=-INF;
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
scanf("%d",&e[i][j]);
sum[i][j]=sum[i-1][j]+e[i][j];
}
}
for(k=1;k<=n;k++)//枚举子矩阵高度
{
for(i=1;i<=n-k+1;i++)//子矩阵开始行
{
for(j=1;j<=n;j++)
f[j]=sum[i+k-1][j]-sum[i-1][j];
ans=max(ans,fun(n));
}
}
printf("%d\n",ans);
}
return 0;
}