OJ链接: 历届试题 最大子阵
问题描述
给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
其中,A的子矩阵指在A中行和列均连续的一块。
输入格式
输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
接下来n行,每行m个整数,表示矩阵A。
输出格式
输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。
样例输入
3 3
-1 -4 3
3 4 -1
-5 -2 8
样例输出
10
样例说明
取最后一列,和为10。
数据规模和约定
对于50%的数据,1<=n, m<=50;
对于100%的数据,1<=n, m<=500
,A中每个元素的绝对值不超过5000。
- 思路
这是一个二维化的最大连续子段和,想象出一个矩阵,我们在列的方向把他压扁,就实现了二维化一维。但是要怎么压扁呢?要像冒泡排序一样进行一个二重循环,再加上一个对列的循环,就大功告成了。
- 三重循环
①下限行循环
②上限行循环
③列循环
这么说还是有点抽象,看代码吧:
- 代码:
#include <bits/stdc++.h>
#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 600
#define MAX ((1<<30)-1)
#define V vector<int>
using namespace std;
int sum[LEN][LEN]; //列前缀和
int main(){
// freopen("最大子阵.txt","r",stdin);
int N,M;
I("%d%d",&N,&M);
int i,j,k,t;
F(i,1,N+1)F(j,1,M+1){
I("%d",&t);
sum[i][j]=sum[i-1][j]+t; //对上一行进行累加,构建列前缀和
}
int ans=-MAX; //结果(初试化为无穷小)
F(i,1,N+1){ //下限行循环
F(j,0,i){ //上限行循环
int tsum=0;//当前局面的最大子段和
int tans=-MAX;
F(k,1,M+1){ //列循环
int num=sum[i][k]-sum[j][k];//一维化为连续最大子段和问题
if(tsum+num<0){ //加上这个数反而小于0了,断开
tsum=0;
tans=max(tans,num);//考虑全为负数的矩阵
}else{
tsum+=num;
tans=max(tans,tsum);
}
}
ans=max(ans,tans);
}
}
O("%d\n",ans);
return 0;
}