一中OJ #1086 最大子矩阵 | 贪心连续子序列 + 平面降维压缩 | 解题报告

该博客详细介绍了如何使用贪心策略和一维连续子序列和的方法解决最大子矩阵问题,将原本的二维矩阵压缩成一维序列,从而将复杂度降低到线性,实现高效求解。
摘要由CSDN通过智能技术生成

一中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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值