最大子数组问题

207 阅读1分钟

求区间[L,R]的最大子区间

朴素做法

预处理前缀和,枚举左右端点维护答案。复杂度O(n2)O(n^2)

优化

采用分治。将区间[L,R]分为[L,Mid]和[Mid+1,R],则最大子区间有以下三种情况:

  1. 完全位于左区间;
  2. 完全位于右区间;
  3. 横跨左右区间;

因此ans=max(Lans,Rans,Midans)ans=max(L_{ans},R_{ans},Mid_{ans})
Lans,RansL_{ans},R_{ans}均在之前求得,因此只需要线性时间求MidansMid_{ans},即从Mid位置向前后分别扫描,维护各自的MAX,最后加和即可。复杂度O(nlogn)O(nlogn)

再优化

运用前缀和的思想,选择第i个数时,要再加上前面的区间[l,r],当且仅当SUM[l,r]0SUM_{[l,r]} \geq0。因此可以从头向前扫,一旦SUM为负就清零,否则就加上当前数字并维护答案。复杂度O(n)O(n)

代码3

#include<bits/stdc++.h>
using namespace std;
const int N=2E5+10;
int n,ans,sum,k=-N;
inline int read()
{
	int ret=0,flag=1;char ch=getchar();
	while(ch<'0' || ch>'9')
	{
		flag=(ch=='-')?-1:1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
	return ret*flag;
}
int main()
{
	n=read();
	for(register int i=1,tmp;i<=n;++i)
	{
		tmp=read();
		k=max(tmp,k);
		sum=(sum+tmp<=0)?0:sum+tmp;
		ans=max(ans,sum);
	}
	cout<<(ans?ans:k)<<endl;
}