队列:最大子序和

题目链接:https://www.acwing.com/problem/content/137/
题意:输入一个长度为n的整数序列,从中找出一段长度不超过m的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是1。
数据范围
1≤n,m≤300000
输入样例:
6 4
1 -3 5 1 -2 3
输出样例:
7
思路:显然我们要在前缀和数组中sum[i]的前m范围内找到一个min,那么以第i个数结尾的m个连续元素的序列的最大子序和就是这个sum[i]-min;最后就只需要遍历一遍寻找答案啦。显然在寻找min的时候我们也可以循环枚举,但这样的时间复杂度就会很高啦;这个时候我们就可以优化一下,用一个队列存m范围内的数(不对弹出和加入来维护),而且如果队列中出现’大小’的两个数,那么前面那个大的数就永远不会用到(比小数先弹出,又不是min)所有整个队列就变成一个严格单调的队列了,而且队头就是我们寻找的min.
代码实现:

#include<iostream>
#include<limits.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;

int n, m;
int q[N];
ll s[N];

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ ){
        cin >> s[i];
        s[i] += s[i - 1];
    }
    int hh = 0, tt = 0;
    ll res = INT_MIN;
    
    for(int i = 1; i <= n; i ++ ){
        //如果队头超出m范围就将它弹出
        if(i - q[hh] > m) hh ++ ;
        res = max(res, s[i] - s[q[hh]]);
        //先删除前面大的数再插入,保证队列单调
        while(hh <= tt && s[q[tt]] >= s[i]) tt -- ;
        q[ ++ tt] = i;
    }
    cout << res << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值