Hlg 1522 子序列的和 <单调队列>
题意:
给一串数字长度n 和 取值长度范围m
还有这串数字..a0, a1, a2, a3, ..., an
求这串数字中在 长度范围m 内和sum 最大的..
思路:
从1~n 遍历每一个数..
当前数字串和S(i) - 以前数字串和中的最小那个S(i-k){1 < k <m}<以前数字串的‘以前’范围是取值长度范围>
‘以前数字串和中的最小那个’可以用 单调队列que 来控制..
取遍历结果中最小那个..
Tips:
主要讲一下什么是单调队列..
单调队列..顾名思义就是 单调增或减的队列..它存的值是对应 和数组s 里的下标..
其中队头元素是 队列s 最大值或最小值的下标..
但是其实这个队列不是实际存在的..
而是用在一个大队列里..找出符合条件的单调小队列..
然后根据这个单调小队列来解题..
找单调小队列靠一个队头指针front 和 一个队尾指针 rear
把下标加入一个大的队列里..
然后根据条件调整队头指针和队尾指针..
而条件就是
①. 队头元素:在 front指针 <= rear 指针的前提下.. 队头元素的下标在给定范围内..
②. 队尾元素:在 front指针 <= rear 指针的前提下.. 若是单调递增队列..即队头元素是最小的..so..新插入的队尾元素的值应该要比原队尾的值大..保证插入新队尾元素后这个队列还是单调递增队列..
若是单调递减队列..即队头元素是最大的..so..新插入的队尾元素的值应该要比原队尾的值小..保证插入新队尾元素后这个队列还是单调递减队列..
eg:
假设数列为:8,7,12,5,16,9,17,2,4,6.N=10,k=3.
那么我们构造一个长度为3的单调递减队列:
首先,那8和它的索引0放入队列中,我们用(8,0)表示,每一步插入元素时队列中的元素如下:
0:插入8,队列为:(8,0)
1:插入7,队列为:(8,0),(7,1)
2:插入12,队列为:(12,2)
3:插入5,队列为:(12,2),(5,3)
4:插入16,队列为:(16,4)
5:插入9,队列为:(16,4),(9,5)
。。。。依此类推
那么f(i)就是第i步时队列当中的首元素:8,8,12,12,16,16,。。。
例子自【转】自:http://blog.csdn.net/Justmeh/article/details/5844650#reply 感觉博主讲得挺详细挺清楚的..就是code貌似有点问题..
最后这道题单调小队列的结果就是 在 取值长度范围m 内和单调递增的 对应下标值..
Code:
1 #include <stdio.h> 2 #include <cstring> 3 #define clr(x) memset(x, 0, sizeof(x)) 4 5 int que[100010]; 6 int s[100010]; 7 8 int buffer(int n, int len) 9 { 10 11 int i, j, k; 12 int front = 0, rear = -1; 13 int res = -99999999; 14 15 for(i = 1; i <= n; ++i){ 16 while(front <= rear && que[front] < i-len) 17 front++; 18 19 while(front <= rear && s[i-1] < s[que[rear]]) 20 rear--; 21 22 que[++rear] = i-1; 23 24 if(res < s[i]-s[que[front]]) 25 res = s[i]-s[que[front]]; 26 } 27 28 return res; 29 } 30 31 int main() 32 { 33 int i, j, k; 34 int num; 35 int n, m; 36 while(scanf("%d %d", &n, &m) != EOF) 37 { 38 clr(que); 39 clr(s); 40 for(i = 1; i <= n; ++i){ 41 scanf("%d", &num); 42 s[i] = s[i-1]+num; 43 } 44 45 printf("%d\n", buffer(n, m)); 46 } 47 return 0; 48 }