最大子序和(单调队列)

题目传送门

题意
给你n个数,求出最大连续子段和,并且该子段长度不超过m,且不能为空子段。

样例输入

6 4
1 -3 5 1 -2 3

样例输出

7

数据范围

1≤n,m≤300000

思路
对于连续的子段和,我们应该会想到是右端点的前缀和减去左端的前缀和,但是如果枚举左右端点,时间复杂度不现实。
偷偷看了眼标签,单调队列,所以去现学了下单调队列。
单调队列可以用双向队列实现,不会双向队列基本操作的可以去看看别的博主的文章。
对于两个下标j,k,如果k>j且sum[k]<sum[j],那么我们可以知道,k一定优于j,因为对于右端点I,sum[i]-sum[k]>sum[i]-sum[j],并且 i 到 k 的距离要小于 i 到 j 的距离,所以k一定优胜与j。
所以我们可以发现,我们从1到n的枚举前缀和,如果发现了后到的下标的前缀和要小于先前队列尾部的前缀和,那么就可以把它挤掉。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
const int INF=0x7fffffff;
const ll LLINF=0x7fffffffffffffff;
const double EPS=1e-10;
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define int long long
int a[N],sum[N];
deque<int>q;
signed main()
{
    IOS;
    //freopen("","r",stdin);
    //freopen("","w",stdout);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
    }
    int res=-INF;
    q.push_back(0);
    for(int i=1;i<=n;i++)
    {
        while(!q.empty()&&q.front()<i-m)
            q.pop_front();
        res=max(res,sum[i]-sum[q.front()]);
        while(!q.empty()&&sum[q.back()]>=sum[i])
            q.pop_back();
        q.push_back(i);
    }
    cout<<res<<endl;
}

发布了93 篇原创文章 · 获赞 9 · 访问量 4204

猜你喜欢

转载自blog.csdn.net/Joker_He/article/details/104547606