TYVJ 1305最大子序和【单调队列】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37867156/article/details/81949894

单调队列:即一个具有单调性的队列,可以递增也可以递减。它的思想是在队列中及时排除一定不是最优解的选择,

题目描述:给定一个长度为N的整数序列(可能有负数),从中找出一段长度不超过M的连续子序列,使得子序列中所有数的和最大。N,M<=3*10^5。

分析:我们先求出前缀和,则连续子序列 [L,R] 中数的和就等于 S[R] - S[L-1]。所以原问题就转换为:找出两个位置 x,y,使得

S[y] - S[x] 最大并且 y - x <= M。

首先我们枚举右端点 i ,当 i 固定时, 问题就变为:找到一个左端点 j,i - M <= j <= i -1 ,并且s[j]最小。

我们可以用一个队列来保存这个序列。从左往右扫描右端点,对每个i执行以下三个步骤: 
1.判断当前队头的数与 i 的距离是否大于M,若超出则出队。
2.此时队头就是右端点为i时的最佳答案(不是最佳的答案在每次第三步更新sum[i]进队的时候都出队了) 
3.不断删除队尾直到队尾的s值小于s[i](因为如果一个s值比s[i]大,他的位置又比i靠左,那么他是没有存在意义的),将s[i]入队; 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int maxn=300000+7;
int a[maxn],sum[maxn];
int n,m;
int q[maxn];
int main(){
	scanf("%d %d", &n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	int l=1,r=1;
	q[1]=0;
	int ans=0;
	for(int i = 1; i <= n; i++){
		while(l <= r&&q[l] < i-m) l++;
		ans=max(ans,sum[i]-sum[q[l]]);
		while(l<=r&&sum[q[r]]>=sum[i]) r--;
		q[++r] = i;
	}
	printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37867156/article/details/81949894