zjm 有乙個長度為 n 的數列和乙個大小為 k 的視窗, 視窗可以在數列上來回移動. 現在 zjm 想知道在視窗從左往右滑的時候,每次視窗內數的最大值和最小值分別是多少. 例如:
數列是 [1 3 -1 -3 5 3 6 7], 其中 k 等於 3.
window position
minimum value
maximum value
[1 3 -1] -3 5 3 6 7-13
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 -3 [5 3 6] 736
1 3 -1 -3 5 [3 6 7]37
input
輸入有兩行。第一行兩個整數n和k分別表示數列的長度和滑動視窗的大小,1<=k<=n<=1000000。
第二行有n個整數表示zjm的數列。
output
輸出有兩行。第一行輸出滑動視窗在從左到右的每個位置時,滑動視窗中的最小值。第二行是最大值。
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
本題核心演算法為單調佇列。
單調佇列與單調棧區別在於,單調棧因為只有棧頂可以操作,所以儲存的內容總是全域性的,而單調佇列兩邊都可以操作,所以儲存的內容可以是區域性的。所以單調佇列可以在這裡用於處理乙個滑動視窗。
兩次遍歷整個陣列,一次遞增佇列,一次遞減佇列,分別計算滑動視窗內最小值、最大值。
(遞增佇列中隊首元素總是滑動視窗內最小值,遞減佇列中隊首元素總是滑動視窗內最大值)
如何維護乙個單調佇列呢?(以單增隊列為例)
遍歷整個陣列:
(1)判斷隊首元素是否仍然屬於滑動視窗。若不屬於,隊首元素出佇列。
(2)判斷目前陣列元素是否可以入佇列。佇列未滿且該元素大於等於隊尾元素,則可以入佇列。否則,將隊尾元素出佇列,直到隊列為空或隊尾元素小於等於該元素,再將該元素入佇列。
維護單調佇列的同時,在恰當位置輸出每個滑動視窗最小值、最大值,即為正確答案。
#include
using
namespace std;
int n=
0,k=0;
int a[
10000000];
int b[
10000000];
//佇列
intmain()
//輸入
//最小值
int front=
0,tail=-1
;for
(int i=
0;i)while
(tail>=front&&a[b[tail]
]>=a[i]
)//入佇列
b[++tail]
=i;if
(i>=k-1)
}printf
("\n");
//最大值
front=
0; tail=-1
;for
(int i=
0;i)while
(tail>=front&&a[b[tail]
]<=a[i]
)//入佇列
b[++tail]
=i;if
(i>=k-1)
}printf
("\n");
return0;
}
輸出的位置一定要確定好,在視窗滑動過程中就找到合適位置輸出,就不必再儲存,浪費記憶體空間了。
雖然是維護兩個單調佇列,但是兩個佇列先後出現,可以指宣告乙個b陣列,反覆使用,通過對引數的控制對其反覆使用,節省空間。
因為需要判斷隊首元素是否還在滑動視窗內,所以單調佇列中必須儲存元素的編號,而非編號的值!
單調佇列 滑動視窗
nkoj 2152 description 給你乙個長度為n n 10 6 的陣列,乙個長為k的滑動的窗體從最左移至最右端,你只能見到視窗的k個數,每次窗體向右移動一位,找出窗體所包含的數字的最大和最小值,如下表所示 k的值為3 視窗位置 最小值 最大值 1 3 1 3 5 3 6 7 1 3 1 ...
滑動視窗 單調佇列
給定乙個大小為n 106的陣列。有乙個大小為k的滑動視窗,它從陣列的最左邊移動到最右邊。您只能在視窗中看到k個數字。每次滑動視窗向右移動乙個位置。以下是乙個例子 該陣列為 1 3 1 3 5 3 6 7 k為3。視窗位置 最小值 最大值 1 3 1 3 5 3 6 7 1 3 1 3 1 3 5 3...
單調佇列 滑動視窗
p1886 滑動視窗 模板 單調佇列 有乙個長為 n 的序列 a,以及乙個大小為 k 的視窗。現在這個從左邊開始向右滑動,每次滑動乙個單位,求出每次滑動後視窗中的最大值和最小值。如果按照暴力方法做的話,每一次判斷相鄰的k個數的最大值和最小值,複雜度為o n k 肯定會超時,因此就想到把每次的最大值和...