Contest Hunter - 1201 最大子序和(单调队列)

题目链接
在这里插入图片描述
1.最大子序和问题,应该是比较常见的,但是这道题需要我们求长度不超过m的最大子序列和。首先不考虑这个条件,对于以j为终点的每一个子序列和,假设它以i为起点(i<=j)我们都可以通过两个前缀和的差s[j]-s[i-1]求得,这样s[i-1]一定是s[1],…,s[j-1]中最小的。但是本题的限制条件,长度不超过m,我们显然不能通过维护前面前缀和的最小值来写

2.很容易想到遍历每一个终点的前1~m个数,但是当m较大时,时间复杂度达到了O(n2),3e5的数据范围显然一定超时。对于每一个终点前面的乱序的前缀和,如何快速找到符合条件的最小呢?这里可以使用单调队列

3.使用双端队列,队列中存储每一个终点前面的起点序号。首先无法优化的操作,就是必须遍历队列去找长度不超过m的最小元素,然后更新ans。但是下面的操作就特别重要了,对于当前终点j,我们找到了s[q[?]]是前缀不超过m的最小前缀和,于是,在当前终点的序号入队之前,我们将队列中大于等于s[q[?]]的元素全部删除,可以证明,在第j+1个终点,我们刚刚找到的最小前缀和是有用的,其他比它大的一定都不会被用到。这样之后,相当于维护
了一个单调队列,不难发现,由于每次的删除,队列的每次查找都是很小的常数级别,而且队列中不会有太多的数。和正常求最大前缀和相似,我们首先要向队列中添加0,对于第一个数,只有减去0才不影响第一个最大前缀和
对于样例来看,前缀和为:
1 -2 3 4 2 5
每次队列的存储的下标对应的前缀和,加粗的为有效的最小值为:
0
0 1
-2
-2 3
-2 3 4
-2 2
-2 2 5

#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x7f7f7f7f
typedef long long ll;
const int maxn=3e5+10;
int s[maxn],q[maxn];
int n,m;

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&s[i]);
        s[i]+=s[i-1];
    }
    int ans=-INF;
    int l=1,r=1;
    q[1]=0;
    for(int i=1;i<=n;i++){
        while(l<=r && q[l]<i-m) l++;
        ans=max(ans,s[i]-s[q[l]]);
        while(l<=r && s[q[r]]>=s[i]) r--;
        q[++r]=i;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值