添加链接描述
思路
1.如果单纯枚举首行,末行,首列,末列来做这道题,就是四重循环,数据最大时,每重循环大概500次,500^4时间复杂度可以达到十的十次方,肯定会超时。所以要考虑能否降循环,或者找到贪心,dp规律。
2.我们思考一下,可以发现。我们可以开一个数组sum,用来记录某个格子头上全部的格子+它本身的和。举个例子,样例的数据是
-1 -4 3
3 4 -1
-5 -2 8
那么sum数组就是
-1 -4 3
2 0 2
-3 -2 10
/sum数组的建立看代码13~24行/
3.建立起sum数组之后,sum的每个元素就代表该列从第一个元素到其自身的和。比如sum数组‘0’这个格子,就是样例数组第二列从第一个元素加到这个位置的和,-4+4=0。‘10’这个格子=3+(-1)+8,其它同理
4.为什么要求这个sum数组?因为我们可以通过这个sum数组求出原数组中任意一列的任意一行元素到任意一行元素的和。比如我要求第一列中,第二行到第三行的和。
这个和=sum[3][1] - sum[1][1]
所以和=-3-(-1)=-2
仔细看,式子中的2需要减1,第三行减去第二行头上一格得到的结果就是第二行到第三行的和
5.枚举首行,末行,当前考虑的列尾。不需要四重循环。三重循环就可以一一列举各种情况了。用最大子段和的思想求出某种首行末行时,以这个元素为列尾得到的最优和。全部的最优和里面最大的就是结果。(所谓最大字段和就是前面小于0就舍弃,大于0就加上,不明白的话可以私信)
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3;
int n, m, sum[N][N], temp, ans = -1e9;
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
cin >> temp;
sum[i][j] = sum[i-1][j] + temp;
}
for(int i = 1; i <= n; i++)//枚举首行
for(int j = i; j <= n; j++)//枚举末行
{
int ans_temp = 0;
for(int k = 1; k <= m; k++)//枚举所有列
{
ans_temp += sum[j][k] - sum[i-1][k];
if(ans_temp > ans)
ans = ans_temp;
if(ans_temp < 0)
ans_temp = 0;
}
}
cout << ans << endl;
return 0;
}