【POJ 2823 Sliding Window】 单调队列

题目大意:给n个数,一个长度为k(k<n)的闭区间从0滑动到n,求滑动中区间的最大值序列和最小值序列。

最大值和最小值是类似的,在此以最大值为例分析。

数据结构要求:能保存最多k个元素,快速取得最大值,更新时删去“过期”元素和“不再有希望”的元素,安放新元素。

单调队列的基本概念百度百科讲得比较清楚了:http://baike.baidu.com/view/3771451.htm

 

我的大致思路是:

1. 每个元素存储为结构体,包含它的秩和值。维护最大长度为k的单调队列,保证所有元素的秩都在区间内,且从首到尾的元素,秩递增,值递减。

2. 读入前k个元素(不存在过期问题),安放每个元素c:从队尾开始往回找到第一个大于它的元素g,将c放到g后面,c成为新的队尾

3. 队首赋给最大值序列的第一个值。

4. 读入k~n-1的元素,每读入一个元素c:

  (1)处理队首的过期元素(每次最多只可能是队首一个元素过期,因为队列长度不超过k,且秩是单调增的)

  (2)安放c(方法同前k个元素)

  (3)将新队首赋给最大值序列的下一个值

5. 输出最大值序列

想清楚了的话,代码还是比较好写的;队列没有封装,简单地用数组+首尾指针实现:

 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4
 5 struct Node
 6 {
 7     int index;
 8     int value;
 9 };
10
11 Node max_q[2000002],min_q[2000002];
12 int max_res[1000002],min_res[1000002];
13 int front_max,front_min;//队首指针
14 int back_max,back_min;//队尾指针
15 int n,k,c;
16
17 int main()
18 {
19     //freopen("c.txt","r",stdin);
20     scanf("%d%d",&n,&k);
21     back_max=back_min=front_max=front_min=0;
22     scanf("%d",&c);
23     max_q[back_max].value=c;
24     min_q[back_min].value=c;
25     max_q[back_max].index=0;
26     min_q[back_min].index=0;//用第一个元素初始化
27     for(int j=1;j<k;j++)//前k个元素
28     {
29         scanf("%d",&c);
30         while(back_max>=0 && max_q[back_max].value<=c) back_max--;
31         max_q[++back_max].value=c;
32         max_q[back_max].index=j;
33
34         while(back_min>=0 && min_q[back_min].value>=c) back_min--;
35         min_q[++back_min].value=c;
36         min_q[back_min].index=j;
37
38     }
39     max_res[0]=max_q[0].value;//区间起始位置的最值
40     min_res[0]=min_q[0].value;
41
42     for(int j=k;j<n;j++)//下标为k到n-1的元素
43     {
44         scanf("%d",&c);
45         if(max_q[front_max].index==j-k) front_max++;
46         if(min_q[front_min].index==j-k) front_min++;
47
48         while(back_max>=front_max && max_q[back_max].value<=c) back_max--;
49         max_q[++back_max].value=c;
50         max_q[back_max].index=j;
51
52         while(back_min>=front_min && min_q[back_min].value>=c) back_min--;
53         min_q[++back_min].value=c;
54         min_q[back_min].index=j;
55
56         max_res[j-k+1]=max_q[front_max].value;//每读入一个元素,更新一次区间,得到一个最值
57         min_res[j-k+1]=min_q[front_min].value;
58     }
59
60     for(int j=0;j<n-k+1;j++)
61         printf("%d ",min_res[j]);
62     printf("\n");
63     for(int j=0;j<n-k+1;j++)
64         printf("%d ",max_res[j]);
65     printf("\n");
66     return 0;
67 }

OJ的结果是这样的(G++会超时,尚不明原因):

最开始没有考虑过期的问题,考虑之后担心队列不够长,需不需要写成循环的;但稍加分析会发现,front指针后移只发生在删除队首过期元素时,最多只发生n-k次,那么数组开到2n就可以了。

由于是不循环的队列,只需front和back两个指针就可完成所有需要的操作。(之前因为和一个计数变量混用,并是在边界判断时WA了很多次)

把前k个元素和之后的元素分开处理是为了考虑方便,AC了之后试图把它们合并起来然后并是又WA了。。。看来有时候不必过于追求代码的简炼,初学还是清晰更重要。

时间: 2024-08-29 01:31:05

【POJ 2823 Sliding Window】 单调队列的相关文章

算法:uva-1427 Parade (单调队列优化dp)

题意 F城由n+1个横向路和m+1个竖向路组成.你的任务是从最南边的路走到最北边的路,使得走过 的路上的高兴值和最大(注意,一段路上的高兴值可以是负数).同一段路不能经过两次,且不能从北往南 走.另外,在每条横向路上所花的时间不能超过k. 思路 这题在uva和LA上又是不能评测, 于 是在hdu和poj上评测了这题 这题状态比较容易想到, f(i, j)表示走到第i行第j点的最大价值 对于 每一点,可以从下一行的走上来,也可以从左边走过来,也可以从右边走过来 设L(i, j)表示第i行从左边 走

[LeetCode]239.Sliding Window Maximum

题目 Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. For example, Given

[LeetCode] Sliding Window Maximum

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. For example, Given num

浅谈单调队列、单调栈_C 语言

初谈这个话题,相信许多人会有一种似有所悟,但又不敢确定的感觉.没错,这正是因为其中"单调"一词的存在,所谓单调是什么,学过函数的people都知道单调函数或者函数的单调性,直白一点说单调就是一直增或一直减.例如:1,3,5,9就是一个单调增数列,数列中不存在后一个数比前一个数小的现象.那么同样,在这里谈到的话题也有类似特点. 先说一下单调队列吧!      单调队列,就是一个符合单调性质的队列,它同时具有单调的性质以及队列的性质.他在编程中使用频率不高,但却占有至关重要的地位.它的作用

HDU 3415(单调队列)

Max Sum of Max-K-sub-sequence Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4080 Accepted Submission(s): 1453 Problem Description Given a circle sequence A[1],A[2],A[3]......A[n]. Circle sequence

数据结构专题

打星号的表示个人认为比较经典,或是算法比较好的题目   1195 Mobile phones 树状数组 1455 1521 Entropy huffman 1703 Find them, Catch them 并查集 1785 Binary Search Heap Construction 1794 Castle Walls 逆序对 1961 Period KMP重复因子 1984* Navigation Nightmare 并查集+坐标平移 1986* Distance Queries LCA

POJ题目分类

初期: 一.基本算法:      (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.      (4)递推.      (5)构造法.(poj3295)      (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996) 二.图算法:      (1)图的深度优先遍历和广度优先遍历.      (2)最短路径算法(dijkstra,bellman-ford

poj分类

初期: 一.基本算法:      (1)枚举. (poj1753,poj2965)      (2)贪心(poj1328,poj2109,poj2586)      (3)递归和分治法.      (4)递推.      (5)构造法.(poj3295)      (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996) 二.图算法:      (1)图的深度优先遍历和广度优先遍历.      (2)最短路径算法(dijkstra,bellman-ford

Flink 原理与实现:Window 机制

Flink 认为 Batch 是 Streaming 的一个特例,所以 Flink 底层引擎是一个流式引擎,在上面实现了流处理和批处理.而窗口(window)就是从 Streaming 到 Batch 的一个桥梁.Flink 提供了非常完善的窗口机制,这是我认为的 Flink 最大的亮点之一(其他的亮点包括消息乱序处理,和 checkpoint 机制).本文我们将介绍流式处理中的窗口概念,介绍 Flink 内建的一些窗口和 Window API,最后讨论下窗口在底层是如何实现的. 什么是 Win