1. 程式人生 > >Sliding Window POJ - 2823 (單調佇列)

Sliding Window POJ - 2823 (單調佇列)

題目連結
在這裡插入圖片描述

這道題是一道非常經典的單調佇列題目.
單調佇列是一種單調遞增或單調遞減的佇列結構, 在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; }