POJ2823 滑動視窗 (單調佇列)
阿新 • • 發佈:2022-04-04
來學習一下單調佇列:
他只可以從隊尾入隊,但可以從隊尾或隊首出隊,來維護佇列的單調性。單調佇列有兩種單調性:元素的值單調和元素的下標單調。
單調佇列可以用來優化DP。狀態轉移方程形如dp[i]=min{dp[j]+f[j]},i-a<=j<=i-b。i增加1,j的上下界都增加1,即加入一個新的決策到候選集中,要把過時的決策剔除。當決策的取值範圍的上下界均單調變化時,每個決策在候選集合中插入或刪除最多一次,我們就可以用單調佇列來優化。
回到這個題目:
用Min[i]和Max[i]表示以i結尾的大小為k的視窗中的最值。
以Min[i]為例,Min[i]=min{aj} ,i-k+1<=j<=i; i加1,j的上下界同時加1。Min用單調遞增的佇列維護。
(1)單調遞增的佇列,隊頭元素最小;
(2)待入隊的元素<=隊尾元素,隊尾元素出隊,直到滿足單調性或佇列為空,將此元素入隊;
(3)隊頭元素下標<i-k+1,說明過時了,要刪去。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define MAXN 1000010 4 int a[MAXN],Min[MAXN],Max[MAXN],Q[MAXN],n,k; 5 6 void get_min(){//求最小 7 int st=0,ed=0; 8 Q[ed++]=1; 9 Min[1]=a[1]; 10 for(int i=2;i<=n;i++){ 11 while(st<ed&&a[i]<a[Q[ed-1]]) ed--;//刪除隊尾元素 12 Q[ed++]=i;//插入隊尾元素 13 while(st<ed&&Q[st]<i-k+1) st++;//刪除隊首過時元素 14 Min[i]=a[Q[st]];//此時答案就是隊首元素 15 } 16 } 17 18 void get_max(){//求最大 19 int st=0,ed=0; 20Q[ed++]=1; 21 Max[1]=a[1]; 22 for(int i=2;i<=n;i++){ 23 while(st<ed&&a[i]>a[Q[ed-1]]) ed--; 24 Q[ed++]=i; 25 while(st<ed&&Q[st]<i-k+1) st++; 26 Max[i]=a[Q[st]]; 27 } 28 } 29 30 int main(){ 31 scanf("%d%d",&n,&k); 32 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 33 get_min();get_max(); 34 for(int i=k;i<n;i++) cout<<Min[i]<<" "; cout<<Min[n]<<endl; 35 for(int i=k;i<n;i++) cout<<Max[i]<<" "; cout<<Max[n]<<endl; 36 return 0; 37 }