这个题目属于常见题型,给定一组环状数列,求出其中长度不超过k的数字之和的最大值。。
用到了前缀和的技巧,比如要求i,j间所有数字之和,可以提前在输入的时候计算出从0~i的数字之和及从0~j的数字之和,那么sum[i~j]=sum[j]-sum[i];
现在的问题是求出最大的sum[i~j].那么我们可以固定sum[j]的值,当sum[i]最小时,整个式子值最大。。显然会用到单调递增队列,不断维护进队出队操作即可,
保持单调递增队列的单调性不被破坏,则队头元素始终是最小的值;
至于sum[j]不断枚举就行了,至此问题得到完美解决。
下面是我的代码,调了半天才发现一个极其SB的错误,维护进队操作时,应该用sum[q[tail]]和sum[cur]比较进行删除队尾元素的操作,我不知道为何手贱的写成了sum[q[head]]......T_T,地球人已经阻止不了我犯二的脚步了~~QAQ
/****************************** * author :crazy_石头 * data structure: 单调队列 * created time:2013/112/31 23:27 * Pro:HDOJ 3415 * Judge Status:Accepted * Memory:2098K * Time:171MS * PS:可以优化输入输出加速,ORZ * 这题也很基础,不过多了起始点和结束点的维护罢了; *******************************/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; #define rep(i,h,m) for(int i=(h);i<=(m);i++) #define INF 0x3fffffff const int maxn=100000+5; int sum[maxn<<1],q[maxn<<1],a[maxn<<1];//q存储下标,元素的值可以通过访问下标间接访问到; int res,test; int head,tail,n,k; int start,end; inline void Enqueue(int cur)//维护单调递增队列入队过程; { while(head<=tail&&sum[q[tail]]>sum[cur]) tail--;//当队列不空且待插入元素小于队尾元素时,删除队尾元素,以维护队列单调递增性质; q[++tail]=cur;//记录下标; } inline void Dequeue(int cur)//不断维护该操作即可; { while(head<=tail&&q[head]<cur-k) head++;//当队列不空且队头不在给定范围之内时删除队头元素; if(sum[cur]-sum[q[head]]>res)//不断更新res; { res=sum[cur]-sum[q[head]]; start=q[head]+1;//记录起点和终点; end=cur; } } int main() { scanf("%d",&test); while(test--) { memset(sum,0,sizeof(sum)); scanf("%d%d",&n,&k); head=1,tail=0; res=-INF; rep(i,1,n) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i];//维护前缀和; } rep(i,n+1,n+k) sum[i]=sum[i-1]+a[i-n];//处理循环数列的方法:在其后面复制一串数字; Enqueue(0); rep(i,1,n+k) { Dequeue(i); Enqueue(i); } printf("%d %d %d\n",res,start>n?start-n:start,end>n?end-n:end); } return 0; } |
* This source code was highlighted byYcdoiT. ( style: Fog )