问题简述
给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
样例说明
取最后一列,和为10。
数据规模和约定
对于100%的数据,1< =n, m< =500,A中每个元素的绝对值不超过5000。
输入
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
输出
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
样例输入
3 3
-1 -4 3
3 4 -1
-5 -2 8
样例输出
10
原理其实跟求一段数字的最大子串问题很像,关键步骤都是sum<0时候的清零
#include<stdio.h>
long long n,m,num[520][520],ans;
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&num[i][j]);
num[i][j]+=num[i][j-1];//求行的逐个的总和
}
}
ans=num[1][1];
for(int i=1;i<=m;i++){//因为是以行的总和,所以数m,如果是列那就是n
//第一层的i负责制造左边界
for(int j=i;j<=m;j++){//j=i跟j++负责 制造右边界
long long s=0;
for(int k=1;k<=n;k++){//k负责制造上下边界(当s加到负数之后,自动隔开变成新的上边界
//在这一层循环之中只考虑j到i-1列不变的情况下,1到n行之间,总和最大的那几行
s+=num[k][j]-num[k][i-1];//第k行的第j个减去第i个(i从0长到m)而j一直是i
//(每完成一趟循环之后j也会递增)
if(s>ans) ans=s;
if(s<0) s=0;
}
}
}
printf("%lld\n",ans);//
return 0;
}
以行为计算单位执行,接下来看看以列为计算单位的,原理是一样的,但是从另一个角度计算,两者无明显差异,时间复杂度上理论上是一样的,但是根据实际的m、n值可能是不同的
#include<iostream>
#include<stdio.h>
long long n,m,sum, ans,num[520][520];
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%lld",&num[i][j]);
num[i][j]=num[i-1][j]+num[i][j];//这里是纵列上的相加
//这里为了方便,不考虑输入进来全是负数的情况
}
}
ans=num[1][1];//设立一个极小值
for(int i=1;i<=n;i++) {//i负责构成上边界
for(int j=i;j<=n;j++){//j负责构成下边界
sum=0;//每次清0
for(int k=1;k<=m;k++){
sum=sum+num[j][k]-num[i-1][k];//k的更新负责左右边界
if(sum>ans) ans=sum;
if(sum<0) sum=0;//如果小于0了直接开始新的左边界并清零
}
}
}
printf("%lld\n",ans);
return 0;
}
该说明的都在注释上了,也是给自己的一个备忘