單調佇列 POJ2823
看這個問題:An array of size n ≤ 106 is given to you. 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 rightwards by one position.Your task is to determine the maximum and minimum values in the sliding window at each position.
也就是有一個數列a,要求你求數列b和c,b[i]是a[i]…a[i+w-1]中的最小值,c[i]是最大值。如果a是1,3,-1,-3,5,3,6,7,則b為-1,-3,-3,-3,3,3,c為3,3,5,5,6,7。
這個問題相當於一個數據流(數列a)在不斷地到來,而資料是不斷過期的,相當於我們只能儲存有限的資料(sliding window中的資料,此題中就是視窗的寬度w),對於到來的查詢(此題中查詢是每時刻都有的),我們要返回當前滑動視窗中的最大值\最小值。注意,元素是不斷過期的。
解決這個問題可以使用一種叫做單調佇列的資料結構,它維護這樣一種佇列:
a)從隊頭到隊尾,元素在我們所關注的指標下是遞減的(嚴格遞減,而不是非遞增),比如查詢如果每次問的是視窗內的最小值,那麼佇列中元素從左至右就應該遞增,如果每次問的是視窗內的最大值,則應該遞減,依此類推。這是為了保證每次查詢只需要取隊頭元素。
b)從隊頭到隊尾,元素對應的時刻(此題中是該元素在數列a中的下標)是遞增的,但不要求連續,這是為了保證最左面的元素總是最先過期,且每當有新元素來臨的時候一定是插入隊尾。
滿足以上兩點的佇列就是單調佇列,首先,只有第一個元素的序列一定是單調佇列。
那麼怎麼維護這個單調佇列呢?無非是處理插入和查詢兩個操作。
對於插入,由於性質b,因此來的新元素插入到佇列的最後就能維持b)繼續成立。但是為了維護a)的成立,即元素在我們關注的指標下遞減,從隊尾插入新元素的時候可能要刪除隊尾的一些元素,具體說來就是,找到第一個大於(在所關注指標下)新元素的元素,刪除其後所有元素,並將新元素插於其後。因為所有被刪除的元素都比新元素要小,而且比新元素要舊,因此在以後的任何查詢中都不可能成為答案,所以可以放心刪除。
對於查詢,由於性質b,因此所有該時刻過期的元素一定都集中在隊頭,因此利用查詢的時機刪除隊頭所有過期的元素,在不含過期元素後,隊頭得元素就是查詢的答案(性質a),將其返回即可。
由於每個元素都進隊出隊一次,因此攤銷複雜度為O(n)。
POJ2823就是上面描述的那道題。
下面是程式碼,很無聊的是這道題竟然卡scanf和printf,如果用cin,cout就會超時,覺得有點無聊。
1: #include <iostream>
2: #include <vector>
3:
4: using namespace std;
5:
6: struct elem
7: {
8: int v;
9: int p;
10: };
11:
12: int main()
13: {
14: int l,w;
15: cin>>l>>w;
16:
17: elem* q1 = new elem[l+w];
18: elem* q2 = new elem[l+w];
19:
20: int num,max;
21:
22: int head1 = 0,tail1 = 0;
23: int head2 = 0,tail2 = 0;
24: cin>>num;
25: q1[tail1].v = num;
26: q1[tail1].p = 0;
27:
28: q2[tail2].v = num;
29: q2[tail2].p = 0;
30:
31: vector<int> ans1,ans2;
32:
33: for (int t = 1;t < w;t++)
34: {
35: scanf("%d",&num);
36:
37: while (head1 <= tail1 && q1[tail1].v >= num)
38: --tail1;
39: q1[++tail1].v = num;
40: q1[tail1].p = t;
41:
42: while (head2 <= tail2 && q2[tail2].v <= num)
43: --tail2;
44: q2[++tail2].v = num;
45: q2[tail2].p = t;
46: }
47:
48: ans1.push_back(q1[head1].v);
49: ans2.push_back(q2[head2].v);
50:
51: for (int t = w;t < l;t++)
52: {
53: scanf("%d",&num);
54:
55: {
56:
57: while (head1 <= tail1 && q1[tail1].v >= num)
58: --tail1;
59: q1[++tail1].v = num;
60: q1[tail1].p = t;
61:
62: while (t - q1[head1].p >= w)
63: ++head1;
64:
65: ans1.push_back(q1[head1].v);
66: }
67:
68: {
69: while (head2 <= tail2 && q2[tail2].v <= num)
70: --tail2;
71: q2[++tail2].v = num;
72: q2[tail2].p = t;
73:
74: while (t - q2[head2].p >= w)
75: ++head2;
76:
77: ans2.push_back(q2[head2].v);
78: }
79: }
80:
81: delete []q1;
82: delete []q2;
83:
84: for (vector<int>::iterator it = ans1.begin();it != ans1.end();it++)
85: {
86: printf("%d",*it);
87: if (it != ans1.end()-1)
88: printf(" ");
89: else
90: printf("\n");
91: }
92:
93: for (vector<int>::iterator it = ans2.begin();it != ans2.end();it++)
94: {
95: printf("%d",*it);
96: if (it != ans2.end()-1)
97: printf(" ");
98: else
99: printf("\n");
100: }
101: }