《算法竞赛进阶指南》0x12 单调队列 不超过M长度的最大子段和

题目链接:https://www.acwing.com/problem/content/description/137/

首先考虑朴素算法,对于一个端点i,考虑前面的[i-m,i-1]的前缀和,sum[i]-minsum[j] 1<=i<=n,可以维护一个长度为M的队列,对于下一个i,搜索队列中的最小值,将i插入,将前面超出长度M的部分移除。

但是这样的话时间复杂度很高。对于正在维护的队列,考虑到对于k<j<i,如果有sum[k]>=sum[j],那么k永远不会比j更优,所以队列中只需要维护单调的数即可。这就是单点队列的应用。

时间复杂度是O(n)

代码:

#include<bits/stdc++.h>
using namespace std;
#define maxn 3000010
int n,m;
int sum[maxn],q[maxn];
int main(){
    int l=1,r=1;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        sum[i]=sum[i-1]+x;
    }
    q[1]=0;//队列中存放的是所有的决策位置,初始时,长度不满M,从[1,i]决策 
    int ans=-0x7f7f7f7f;
    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; 
    }
    cout<<ans<<endl;
}

 

posted @ 2020-06-17 13:29  WA自动机~  阅读(204)  评论(0编辑  收藏  举报