Sliding Window(单调队列讲解与例题)

来源:互联网 发布:西安mac 编辑:程序博客网 时间:2024/06/04 23:48

Sliding Window

Time Limit:5000MS  Memory Limit:128000K

Description

An array of size n ≤ 1000000 is given toyou. There is a sliding window of size k which is moving from the very left ofthe array to the very right. You can only see the k numbers in the window. Eachtime the sliding window moves rightwards by one position. Following is anexample: 
The array is [1 3 -1 -3 5 3 6 7], and k is 3. 
Window position Minimum value Maximum value 
[1 3 -1] -3 5 3 6 7 -1 3 
1 [3 -1 -3] 5 3 6 7 -3 3 
1 3 [-1 -3 5] 3 6 7 -3 5 
1 3 -1 [-3 5 3] 6 7 -3 5 
1 3 -1 -3 [5 3 6] 7 3 6 
1 3 -1 -3 5 [3 6 7] 3 7 

Your task is to determine the maximum and minimum values in the sliding windowat each position.

Input

The input consists of two lines. The firstline contains two integers n and k which are the lengths of the array and thesliding window. There are n integers in the second line.

Output

There are two lines in the output. Thefirst line gives the minimum values in the window at each position, from leftto right, respectively. The second line gives the maximum values.

Sample Input

8 3

1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3

3 3 5 5 6 7

   大致意思是:给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,你的任务是找出窗体每个时刻中的最大值与最小值。

   如果打一遍模拟,时间复杂度显然是O(n^2),对于这题的数据是不可能通过的;

   我们仔细想想可行的优化,不难发现,在模拟的过程中,有一些位置总是被重复枚举;那么,可以把窗体的每一个时刻的最大值与最小值及其所在位置存下来;又因为每次窗体只移动一格,所以直接判断上一时刻的最值所在位置是否在当前窗体中,若在,则直接将其与新加入窗体的元素进行比较更新(如果相等,越往右的越优,因为在后面的比较中它可用次数更多),这种情况复杂度为O(1);若不在,就老老实实地在当前窗体中全部枚举一次,这种情况时间复杂度为O(n)。总得来说,时间复杂度要小于O(n^2)。

   上述思路其实就和单调队列很接近了,单调队列和普通的队列一样,有队首与队尾,越靠近队首的元素越优。对于此题,可以分别创建一个最大值单调队列与一个最小值单调队列,在窗体移动时,先检查队首元素是否在队列中,不在则队首移动一位;然后对于新加入的元素,将它和当前队尾元素依次比较,若更优则让当前队尾元素出队,直至队列为空或队尾元素比当前元素更优为止。由于每个元素只会入队一次,故单调队列的时间复杂度是线性的,即O(n).

参考代码如下:

#include<cstdio>using namespace std;int n,k,head,tail;int a[1000001];int que[1000001];int label[1000001];int main(){scanf("%d%d",&n,&k);for (int i=1;i<=n;i++)scanf("%d",&a[i]);head=1;tail=0;for (int i=1;i<=n;i++){while (head<=tail&&i-label[head]>=k) head++;while (head<=tail&&que[tail]>=a[i]) tail--;que[++tail]=a[i];label[tail]=i;if (i>=k) printf("%d ",que[head]);}printf("\n");head=1;tail=0;for (int i=1;i<=n;i++){while (head<=tail&&i-label[head]>=k) head++;while (head<=tail&&que[tail]<=a[i]) tail--;que[++tail]=a[i];label[tail]=i;if (i>=k) printf("%d ",que[head]);}}