洛谷 P1886 滑動視窗 /【模板】單調佇列
阿新 • • 發佈:2020-11-04
普及演算法都不會,參加個錘子CSP—S
思路
完全剽竊別人的思路,話說回來,我要是會還學它幹啥。單調佇列,顧名思義,佇列裡的元素都是有單調性的,通過維護單調性,優化時間複雜度。幹說其實根本沒用,直接看題。首先根據題解的思路,求最小值的時候將佇列內元素從小到大排列,這樣每次查詢最小值的時候,只需要彈出隊首即可。然後我們考慮怎麼樣維護單調佇列。當來了一個新數,我們就從後往前找(就是從大到小),如果這個新數比佇列中的那些元素都大,那麼那些大的元素就已經不可能作為最小值的候選人了,所以直接彈出佇列即可。每次迴圈還要檢查一遍佇列的跨度,如果隊首和當前的序號相差過遠了,那麼就只好忍痛割愛,彈出最小的元素了。不難發現,普通的佇列是無法實現單調佇列的(我不會告訴你我想了一個小時才發現了這個道理),因為它既要彈出隊首元素也要彈出隊尾元素,這就需要手寫一個雙端佇列來實現。其實並不難寫,就是隊尾也可以彈出元素罷了。但是程式碼實現還是有一些細節的。
程式碼
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstdlib> #include<ctime> #include<cstring> #include<queue> using namespace std; typedef long long ll; int n,m; ll a[1000005]; int q[1000005],p[1000005]; void _minn(){ int head=1,tail=0;//這樣初始化,是因為佇列內一開始是空的,而佇列為空時就相當於是head>tail for(int i=1;i<=n;i++){ while(head<=tail&&q[tail]>=a[i]){//維護單調性 tail--; } q[++tail]=a[i]; p[tail]=i; if(p[head]<=i-m) head++;//“視窗”長度過長,被迫拋棄最小元素(如果將這一行加到while迴圈前面的話,則需要加上head<=tail這一個條件。否則的話若區間長度為1,則第一次迴圈後head=2,tail=0,喜提RE) if(i>=m) printf("%lld ",a[p[head]]); } printf("\n"); } void _maxx(){ int head=1,tail=0; for(int i=1;i<=n;i++){ while(head<=tail&&q[tail]<=a[i]){ tail--; } q[++tail]=a[i]; p[tail]=i; if(p[head]<=i-m) head++; if(i>=m) printf("%lld ",a[p[head]]); } printf("\n"); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } _minn(); _maxx(); return 0; }