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:

 

View 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 }

 

 

 

转载于:https://www.cnblogs.com/Griselda/archive/2012/08/10/2632428.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值