1. 程式人生 > >openjudge滑動視窗(單調遞增遞減佇列)

openjudge滑動視窗(單調遞增遞減佇列)

滑動視窗(from openjudge)
  解決這個問題的時候,會碰到超時的問題。在資料量很大的情況下,不難想到需要用scanfprintf進行輸入輸出,但是即便是這樣也會導致超時,所以就需要在演算法上做出改變。這也就是這道題的重點:遞增/遞減佇列。
 謂遞增(遞減)佇列想要實現的就是佇列中的元素是有序排列的。在一開始我們會想要直接利用STL庫中的優先佇列實現這個事情,但是問題在於我們難以進入到優先佇列的中間進行操作,只能對頭和尾進行操作,在面對這個題裡面對於視窗長度的限制的時候很難完成任務,所以我們選擇自己實現一個雙端佇列的遞增(遞減)佇列結構。  
 個結構模擬一個佇列,利用val儲存資料,front代表隊列頭,back代表隊列尾,在佇列為空的時候f

ront=back。在這個佇列中最核心的操作就是push。push的作用是加入一個新元素。在本題中,以隊頭元素最大的佇列為例,push需要實現如下的工作:首先,如果從隊頭開始的元素已經在視窗範圍之外,那麼就將其刪除(front+1);其次,如果從隊尾開始的元素小於我們現在要加入佇列的數,那麼將其出佇列(back-1)。   
 在處理的過程中,需要注意以下兩個問題:
1. 因為是對陣列中的數字操作,為了實現判斷數字是否在視窗之內,我們在佇列中儲存的是數字在陣列中的索引(具體可看程式碼實現)
2. 注意在實現從隊首、隊尾操作過程中的邊界判斷(front<b
ack
)
程式碼實現如下

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2000000;
int num[N/2];          /*儲存輸入資料*/
int ansup[N/2];        /*儲存視窗中的大元素答案*/
int ansdown[N/2];      /*儲存視窗中的小元素答案*/
//遞減佇列,找大元素
struct QueueUp {
    int val[N];
    int window;
    int
front, back; QueueUp(int w) { window = w; front = 0; back = 0; } //x傳入的是在num中的索引 int push(int x) { //佇列為空 if (front == back) { val[front] = x; back++; return x; } int tmp = num[x]; //刪掉視窗外的元素 while (front < back && x - val[front] >= window) front++; //刪掉從末尾開始的小元素 while (back - 1 >= front && tmp >= num[val[back - 1]]) back--; val[back] = x; back++; return val[front]; } }; //遞增佇列,找小元素 struct QueueDown { int val[N]; int window; int front, back; QueueDown(int w){ window = w; front = back = 0; } //x傳入的是在num中的索引 int push(int x) { if (front == back) { val[front] = x; back++; return x; } int tmp = num[x]; while (front < back && x - val[front] >= window) front++; while (back - 1 >= front && tmp <= num[val[back - 1]]) back--; val[back] = x; back++; return val[front]; } }; int main() { int n, k; cin >> n >> k; QueueDown down(k); QueueUp up(k); for (int i = 0; i < k - 1; i++) { scanf("%d", num + i); up.push(i); down.push(i); } int count = 0; for (int i = k - 1; i < n; i++) { scanf("%d", num + i); ansdown[count] = down.push(i); ansup[count] = up.push(i); count++; } for (int i = 0; i < count; i++) printf("%d ", num[ansdown[i]]); cout << endl; for (int i = 0; i < count; i++) printf("%d ", num[ansup[i]]); cout << endl; return 0; }

至此完結~~