《算法竞赛进阶指南》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; }
每一个不曾起舞的日子,都是对生命的辜负。