[POJ-2823] -Sliding Window

Sliding Window
Time Limit: 12000MS   Memory Limit: 65536K
Total Submissions: 56028   Accepted: 16112
Case Time Limit: 5000MSnode

An array of size  n ≤ 10  6 is given to you. There is a sliding window of size  kwhich 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 rightwards by one position. Following is an example: 
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 window at each position. ios

Input數組

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

Output數據結構

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

Sample Inputspa

8 3
1 3 -1 -3 5 3 6 7

Sample Outputcode

-1 -3 -3 -3 3 3
3 3 5 5 6 7

題意: 給定一行數,共N個。有一個長度爲K的窗口從左向右滑動,窗口中始終有K個數字,窗口每次滑動一個數字。求各個時刻窗口中的最大值和最小值。blog

 

1、概念介紹
一、 雙端隊列
雙端隊列是一種線性表,是一種特殊的隊列,遵照先進先出的原則。雙端隊列支持如下4種操做:隊列

(1)   從隊首刪除
(2)   從隊尾刪除
(3)   從隊尾插入
(4)   查詢線性表中任意一元素的值
二、 單調隊列
單調隊列是一種特殊的雙端隊列,其內部元素具備單調性。最大隊列與最小隊列是兩種比較經常使用的單調隊列,其內部元素分別是嚴格單調遞減(不是非遞增)和嚴格單調遞增(不是非遞減)的。get

單調隊列的經常使用操做以下:input

(1) 插入:若新元素從隊尾插入後會破壞單調性,則刪除隊尾元素,直到插入後再也不破壞單調性爲止,再將其插入單調隊列。
(2) 獲取最優(最大、最小)值:訪問隊首元素
如下是一個單調遞增隊列的例子:

隊列大小不能超過3,入隊元素依次爲3,2,8,4,5,7,6,4

3入隊:(3)

3從隊尾出隊,2入隊:(2)

8入隊:(2,8)

8從隊尾出隊,4入隊:(2,4)

5入隊:(2,4,5)

2從隊頭出隊,7入隊:(4,5,7)

7從隊尾出隊,6入隊:(4,5,6)

6從隊尾出隊,5從隊尾出隊,4從隊尾出隊,4入隊:(4)

以上左端爲隊頭,右端爲隊尾。

2、單調隊列的應用
一、最大值的維護:

       好比咱們要維護一個區間爲k的最大值的單調隊列,因爲新插入 的節點他的「生命力」確定比原先已經在隊列中的元素「活」的時間長,將插入元素不斷與隊尾元素比, 若是他大於隊尾元素,那麼tail--將隊尾元素刪掉,(由於目前插入的這個元素值(設爲pos)更大,並且「活」的時間 長,有pos在,隊尾元素的有「生」之年永遠都無法爲最大值,故而直接無視比pos小的隊尾了)。直到對空位置或者 找到了一個比pos大的隊尾。 

這個問題至關於一個數據流(數列a)在不斷地到來,而數據是不斷過時的,至關於咱們只能保存有限的數據(sliding window中的數據,此題中就是窗口的寬度w),對於到來的查詢(此題中查詢是每時刻都有的),咱們要返回當前滑動窗口中的最大值最小值。注意,元素是不斷過時的。

解決這個問題可使用一種叫作單調隊列的數據結構,它維護這樣一種隊列:

a)從隊頭到隊尾,元素在咱們所關注的指標下是遞減的(嚴格遞減,而不是非遞增),好比查詢若是每次問的是窗口內的最小值,那麼隊列中元素從左至右就應該遞增,若是每次問的是窗口內的最大值,則應該遞減,依此類推。這是爲了保證每次查詢只須要取隊頭元素。
b)從隊頭到隊尾,元素對應的時刻(此題中是該元素在數列a中的下標)是遞增的,但不要求連續,這是爲了保證最左面的元素老是最早過時,且每當有新元素來臨的時候必定是插入隊尾。
知足以上兩點的隊列就是單調隊列,首先,只有第一個元素的序列必定是單調隊列。

那麼怎麼維護這個單調隊列呢?無非是處理插入和查詢兩個操做。

  對於插入,因爲性質b,所以來的新元素插入到隊列的最後就能維持b)繼續成立。可是爲了維護a)的成立,即元素在咱們關注的指標下遞減,從隊尾插入新元素的時候可能要刪除隊尾的一些元素,具體說來就是,找到第一個大於(在所關注指標下)新元素的元素,刪除其後全部元素,並將新元素插於其後。由於全部被刪除的元素都比新元素要小,並且比新元素要舊,所以在之後的任何查詢中都不可能成爲答案,因此能夠放心刪除。

        對於查詢,因爲性質b,所以全部該時刻過時的元素必定都集中在隊頭,所以利用查詢的時機刪除隊頭全部過時的元素,在不含過時元素後,隊頭得元素就是查詢的答案(性質a),將其返回便可。因爲每一個元素都進隊出隊一次,所以攤銷複雜度爲O(n)。

模板:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstdio>
 4 #include <cstring>
 5 using namespace std;
 6 const int maxn = 1e6 + 5;
 7 struct node
 8 {
 9     int pos, val;
10 }q[maxn];
11 int k, num[maxn], n, Min[maxn], Max[maxn];
12 void getmin()
13 {
14     int head = 1, tail = 0;
15     for(int i = 1; i < k; i++)
16     {
17         while(head <= tail && num[i] < q[tail].val) tail--;
18         tail++;
19         q[tail].val = num[i];
20         q[tail].pos = i;
21     }
22     for(int i = k; i <= n; i++)
23     {
24         while(head <= tail && num[i] < q[tail].val) tail--;
25         tail++;
26         q[tail].val = num[i];
27         q[tail].pos = i;
28         while(i-q[head].pos >= k) head++;
29         Min[i-k] = q[head].val;
30     }
31 }
32 void getmax()
33 {
34     int head = 1, tail = 0;
35     for(int i = 1; i < k; i++)
36     {
37         while(head <= tail && num[i] > q[tail].val) tail--;
38         tail++;
39         q[tail].val = num[i];
40         q[tail].pos = i;
41     }
42     for(int i = k; i <= n; i++)
43     {
44         while(head <= tail && num[i] > q[tail].val) tail--;
45         tail++;
46         q[tail].val = num[i];
47         q[tail].pos = i;
48         while(i - q[head].pos >= k) head++;
49         Max[i-k] = q[head].val;
50     }
51  
52 }
53 int main()
54 {
55     while(~scanf("%d%d", &n, &k))
56     {
57         for(int i = 1; i <= n; i++) scanf("%d", &num[i]);
58         getmin();
59         for(int i=0; i<=n-k; i++) printf("%d%c",Min[i],i==n-k?'\n':' ');
60         getmax();
61         for(int i=0; i<=n-k; i++) printf("%d%c",Max[i],i==n-k?'\n':' ');
62     }
63     return 0;
64 }

用優先隊列寫的:操做的是下標

 1 #include<algorithm>
 2 #include<queue>
 3 #include<vector>
 4 using namespace std;
 5 int a[1000011];//數組數據
 6  
 7 int OutMin[1000011];//最小值
 8  
 9 int OutMax[1000011];//最大值
10  
11 int cnt1=0;
12 int cnt2=0;
13 int n,k;
14  
15 struct cmp1
16 {
17     bool operator()(const int a1,const int a2)
18     {
19         return a[a1]>a[a2];  //這裏太精髓,重定義的是數組元素值,可是隊列存的是下標
20     }
21 };
22 struct cmp2
23 {
24     bool operator()(const int a1,const int a2)
25     {
26         return a[a1]<a[a2];
27     }
28 };
29 priority_queue <int ,vector<int>,cmp1> Q1;  //重定義符號
30 priority_queue <int ,vector<int>,cmp2> Q2;
31 int main()
32 {
33     int i;
34     scanf("%d%d",&n,&k);
35     if(k>n)
36         k=n;
37     for(i=1;i<=n;++i)
38     {
39         scanf("%d",&a[i]);
40     }
41     for(i=1;i<=k;++i)
42     {
43         Q1.push(i);
44         Q2.push(i);
45     }
46     OutMin[cnt1++]=a[Q1.top()];
47     OutMax[cnt2++]=a[Q2.top()];
48     for(i=k+1;i<=n;++i)
49     {
50         Q1.push(i);
51         Q2.push(i);
52         while(i-Q1.top()>=k)
53             Q1.pop();
54         OutMin[cnt1++]=a[Q1.top()];
55         while(i-Q2.top()>=k)
56             Q2.pop();
57         OutMax[cnt2++]=a[Q2.top()];
58     }
59     
60     for(i=0;i<=(n-k);++i)
61     {
62         printf("%d%c", OutMin[i], (i < n - k) ? ' ' : '\n'); 
63         
64     }
65     for(i=0;i<=(n-k);++i)
66     {
67         printf("%d%c", OutMax[i], (i < n - k) ? ' ' : '\n'); 
68     }
69     return 0;
70 }
相關文章
相關標籤/搜索