题目描述:
给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
样例说明:
取最后一列,和为10。
输入:
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
数据规模和约定
对于100%的数据,1< =n, m< =500,A中每个元素的绝对值不超过5000。
输出:
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
样例输入:
3 3
-1 -4 3
3 4 -1
-5 -2 8
样例输出:
10
如果单纯枚举首行,末行,首列,末列来做这道题,就是四重循环,数据最大时,每重循环大概500次,5004时间复杂度可以达到十的十次方,肯定会超时。所以要考虑能否降循环,或者找到贪心,dp规律。
我们创建一个数组sum用来记录记录某个格子头上全部的格子+它本身的和
那么sum数组就是
-1 -4 3
2 0 2
-3 -2 10
我们可以通过这个sum数组求出原数组中任意一列的任意一行元素到任意一行元素的和。比如我要求第一列中,第二行到第三行的和。这个和=sum[3][1]-sum[1][1]
枚举首行,末行,当前考虑的列尾。不需要四重循环。三重循环就可以一一列举各种情况了。用最大子段和的思想求出某种首行末行时,以这个元素为列尾得到的最优和。全部的最优和里面最大的就是结果。
#include <bits/stdc++.h>
int n,m;
int a[505][505],dp[505][505];
int now,max_sum=-999999999,temp_ans;
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]); //读入数据
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(i==1)
{
dp[i][j]=a[i][j];
continue;
}
dp[i][j]=dp[i-1][j]+a[i][j];
}
}
for(int first=1;first<=n;first++) //枚举首行
{
for(int last=first;last<=n;last++) //枚举末行,末行不能比首行小,故初值为first
{
for(int c=1;c<=m;c++) //枚举列的结尾
{
now=dp[last][c]-dp[first-1][c]; //在首行~末行中,某列的和
if(temp_ans>=0) //如果前面的和大于等于0,就将它加上
{
temp_ans=now+temp_ans;
if(temp_ans>max_sum) max_sum=temp_ans; //尝试能否更新最大值
}
else if(temp_ans<0) //如果前面的和小于0,舍弃它
{
temp_ans=now;
if(temp_ans>max_sum) max_sum=temp_ans; //尝试能否更新最大值
}
}
temp_ans=0;
}
}
printf("%d",max_sum);
return 0;
}