一中OJ | #1086 最大子矩阵
时限 1000MS/Case 内存 512MB/Case
题目描述
给出一个 N 行 M 列的整数矩阵,请计算各元素和最大的一个子矩阵的各元素和。
输入格式
第一行两个整数:N 和 M;
接下来是一个 N 行 M 列的一个整数矩阵。
输出格式
一个整数,表示最大子矩阵的各元素和。
样例输入
4 5
-1 -1 0 -1 -1
-1 3 4 -1 0
-2 0 6 8 -2
-3 2 -1 -2 0
样例输出
20
样例解释
图中以(2,2)为左上角,(3,4)为右下角的子矩阵最大,其各元素和为20
数据范围
1<=N<=500,矩阵中元素是32位整数
----------------------------------------------------------
题目分析
求最大子矩阵,能想到的最暴力的办法是枚举左上端点和右下端点,然后慢慢算...
但是其实可以对矩阵进行压缩,随后转化为一维最大连续子序列和的问题
具体思路是假设一个高为x,宽为M的矩阵,然后将其压缩成一个一维序列
把第i行到第i+x-1行的第j列元素求和,压缩到序列中a[j],这样序列中a[k]+a[k+1]+...+a[j]的和就是矩阵中以(i,k)为左上角,(i+x-1,j)为右下角的矩阵之和
那么求最大子矩阵只需要对于每一个(i,i+x-1)求一次当前最大的子矩阵,也就是压缩后的最大连续子序列
统计所有的最大的子序列和的值,最终得到的就是最大子矩阵和
----------------------------------------------------------
关于O(N)找最大连续子序列的方法
从O(N^2)的前缀和方法的基础上优化
对于序列a,求前缀和保存到b数组里面
当搜索到第i个元素的时候,动态维护变量minv为b[1]~b[i]中的最小值
这样对于每一个a[i]就不需要向前搜索最小的b[]啦
----------------------------------------------------------
代码
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <iomanip>
#define inf 0x7f7f7f7f
using namespace std;
bool neg;
int n,m;
long long t,minv,ret,qzh[505][505],q[505],ans=-0x7f7f7f7f7f7f7f7f;
long long mcs(int x,int y)
{
minv=0,ret=0;
memset(q,0,sizeof(q));
for(int i=1;i<=m;i++)
{
q[i]=q[i-1]+qzh[y][i]-qzh[x-1][i];
ret=max(ret,q[i]-minv);
minv=min(minv,q[i]);
}
return ret;
}
void getll(long long &x)
{
neg=false,x=0;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') neg=true;
ch=getchar();
}
while(isdigit(ch))
{
x=x*10+ch-'0';
ch=getchar();
}
if(neg) x*=-1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
getll(t);
qzh[i][j]=qzh[i-1][j]+t;
}
}
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
ans=max(ans,mcs(i,j));
}
}
cout<<ans;
return 0;
}