滑動窗口


單調隊列

 

洛谷 P1886 滑動窗口

好像有個在洛谷上說  ”其實這道題應該叫滑稽窗口“的家伙

然后出於其他原因 成了封禁賬戶

 

by  GeneralLiu

 

給出有n個數的序列

求所有的連續k個數

的最大值 以及 最小值

 

 

思路(就只寫 MAX 了 , MIN 一個道理,懶得寫了)

 

 

目標是  長度為k的窗口中的MAX 且窗口從左向右滑動

 

在滑動過程中  

   有時會進來一個很大的數 比 前面所有都大

     假設 在這個數 出去之前 不會進來比它大的數

     那么在它出去之前 他會一直代表 着窗口的 MAX

     那把窗口分成兩部分 (不考慮 MAX 本身單獨一部分)

    1 MAX 左邊的所有數

    2 MAX 右邊的所有數

   因為 “在它出去之前 他會一直代表 着窗口的 MAX”

   所以 第一部分是 沒有價值的 

   

          而 第二部分 肯定 會比 MAX 晚出去 

    在 MAX 出去后 再 體現價值

 

所以說 一直去掉 “第一部分” 即 沒有價值 的部分

  便維護了一個單調遞減的 “隊列”

  這個“隊列”不是嚴格按照 FIFO first-in-first-out

  你想想 排隊買飯時發現 排錯隊列了

  你難道要 排到你(你到了隊頭)時 再出去嗎?

  所以 這個“隊列”的隊尾也是可以出隊的

 

每次 輸出 隊頭元素 (單調減,隊頭最大)

  等到“該出隊”時(代碼 34 行)再出隊 即可

 

 

代碼

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 1000006
 4 int n,k,que1[N],ans1[N],ans2[N],h1=1,h2=1,t1,t2,que2[N],pos1[N],pos2[N];
 5 int read(){  //  讀入優化 
 6     int ans=0,f=1;
 7     char ch=getchar();
 8     for(;!isdigit(ch);ch=getchar())
 9         if(ch=='-')
10             f=-1;
11     for(;isdigit(ch);ch=getchar())
12         ans=ans*10+ch-'0';
13     return ans*f;
14 }
15 int main(){
16     n=read(),k=read();
17     for(int a,i=1;i<=n;i++){
18           a=read();
19           
20           // 維護 單調減  
21           for(;t1>=h1&&a>=que1[t1];t1--);
22           que1[++t1]=a;
23           pos1[t1]=i;
24           
25           // 維護 單調增 
26           for(;t2>=h2&&a<=que2[t2];t2--);
27           que2[++t2]=a;
28           pos2[t2]=i;
29           
30           if(i>=k){
31               ans1[i]=que1[h1]; //儲存答案 
32               ans2[i]=que2[h2];
33               
34               //該出隊了 
35               if(pos1[h1]==i-k+1)h1++;  
36               if(pos2[h2]==i-k+1)h2++;
37           }
38     }
39     for(int i=k;i<=n;i++) // 最小的 
40         cout<<ans2[i]<<" ";
41     cout<<endl;
42     for(int i=k;i<=n;i++) // 最大的 
43         cout<<ans1[i]<<" ";
44 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM