Sliding Window POJ - 2823 (單調佇列)
阿新 • • 發佈:2019-01-14
這道題是一道非常經典的單調佇列題目.
單調佇列是一種單調遞增或單調遞減的佇列結構, 在C++STL中有priority_queue來實現了, 但較為麻煩只能對隊尾進行操作和訪問, 同時考慮到可能存在超時的因素, 所以在平常做題中我們一般都會選擇自己來實現單調佇列. 對於求類似區間最大/最小的問題, 單調佇列是最佳選擇, 因為它可以在常數複雜度下實現該過程. 即使是對於1.2.3.4.5.6.7.8.9.1(單調遞增)這樣的極端情況, 也僅僅在最後一步才需要遍歷2兩次, 依然是常數複雜度.
在這道題目中應用單調佇列, 我們考慮按照題意如何維護一個定長的單調佇列, 例如單調遞增佇列, 做到以下兩點即可.
- 從隊尾開始清除所有大於新入隊元素的數
- 判斷隊首是否還在視窗內, 若不在則出隊
同時在說一下這道題中其他可能遇到的問題
- 對雜湊表的運用, 可以採用結構體或佇列直接儲存下標
- 本題資料量較大, 請移除程式中所有的cin, 否則會超時
- 多組輸入
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <stdlib.h>
#include <vector>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const LL maxn = 1e6+10;
int q1[maxn], q2[maxn]; //單調遞增佇列和單調遞減佇列(均儲存對應下標)
int num[maxn], n, k, minAns[maxn] , maxAns[maxn];
int main()
{
while(scanf("%d%d",&n,&k)!=EOF){
int head1 = 1, tail1 = 1, head2 = 1, tail2 = 1;
for(int i = 1;i <= n; i++){
//隊首不在滑窗範圍內則刪去;
if(tail1>head1 && q1[head1]<=i-k)
head1++;
if(tail2>head2 && q2[head2]<=i-k)
head2++;
scanf("%d",&num[i]);
//刪去隊尾不符合單調的值
while(tail1>head1 && num[i]<=num[q1[tail1-1]])
tail1--;
q1[tail1++] = i;
while(tail2>head2 && num[i]>=num[q2[tail2-1]])
tail2--;
q2[tail2++] = i;
minAns[i] = num[q1[head1]];
maxAns[i] = num[q2[head2]];
}
for(int i = k; i <= n; i++)
cout << minAns[i] << ' ';
cout << endl;
for(int i = k; i <= n; i++)
cout << maxAns[i] << ' ';
cout << endl;
}
return 0;
}