做dp遇到了单调队列优化的问题,所以便又跑来学这东西来了,单调队列,单调栈。
最近一直在被老师,被同学糗,吼吼。> <
题意:给定一个长度为n的环形序列,让你从中找出一个k长的子序列,使得这段序列的和是所有k长子序列中和最大的那个,输出和,并输出得到这个和时的起始位置跟终止位置。
思路:因为还要记录起始位置跟终止位置,所以很显然队列结点还需要记录下标。我们用一个单调减队列来维护到当前下标时,前面sum的最小值,当然还需要head++使得长度控制在k的范围内。循环判断更新最大值,并记录相应下标就可以了。
View Code
#include <algorithm> #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <queue> #include <map> using namespace std; const int maxn=100000+5; int a[maxn],sum[maxn<<1],head,tail,n,k,st,ed,ans; struct node { int val; int tag; node(int v=0,int t=0):val(v),tag(t){} }q[maxn<<1]; void data_in() { memset(sum,0,sizeof(sum)); memset(a,0,sizeof(a)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(int i=n+1;i<=n+k;i++) sum[i]=sum[i-1]+a[i-n]; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d %d",&n,&k); data_in(); head=1;tail=1; q[tail]=node(0,1); st=ed=1; ans=sum[1]; for(int i=2;i<=n+k;i++) { while(head<=tail&&q[tail].val>sum[i-1]) tail--; q[++tail]=node(sum[i-1],i); while(head<=tail&&q[head].tag<=i-k) head++; int tmp=sum[i]-q[head].val; if(tmp>ans) { ans=tmp; st=q[head].tag; ed=i; } } if(st>n) st-=n; if(ed>n) ed-=n; printf("%d %d %d\n",ans,st,ed); } return 0; }