1. 程式人生 > >單調隊列,單調棧相關

單調隊列,單調棧相關

剔除 地址 logs www. += 夠快 ash 簡單 數組

說起這個話題,應該很多人會有一種似有所悟,但又不敢確定的感覺。

(我差不多就是那樣)

沒錯,這正是因為其中“單調”一詞的存在。

  • 那麽單調是什麽?
  • 學過函數的人都知道單調函數或者函數的單調性吧
  • 其實直白一點說單調,就是一直增或一直減。
  •     eg:1,3,5,9就是一個單調增數列,數列中不存在後一個數比前一個數小的現象。

那麽同樣,在這裏談到的話題也有類似特點。

(一)單調隊列

  其實就是一個符合單調性質的隊列,但它同時具有單調的性質以及隊列的性質。

使用頻率不算高,但卻占有至關重要的地位。它的作用很簡單,就是為了維護一組單調數據,讓我們在運行的過程中能夠快速尋求前k個或後k個中最大或最小的值。

  (二)單調棧

  就是一個符合單調性質的棧並且它具有單調的性質以及棧的性質。


上面的兩個在作用方面是相同的,差別僅是在編程過程中維護的數組的方式不同

(單調隊列有兩個指針,分別代表頭和尾;單調棧有一個指針,代表頭指針)

下面舉個簡單的栗子來解釋單調隊列及單調棧。

eg:有一組數據:1,5,9,4,7,8,6,將他們依此輸入。同時,在某一時刻會讓你求出後n個數中的最大值。

根據題意,我們可以得出這樣一個結論:

    若後一個數大於前一個數,則結果必定不會是前一個數

    (比如現在輸入了1,5,由於1<5,所以無論是後幾個數中的最大值均不會為1)。

    因此,我們只需維護一個單調遞減的數組便可快速求得所需值。

其中數組變化如下:

輸入——1,數組——1;
輸入——5,由於5>1刪去1添入5,數組——5;
輸入——9,由於9>5刪去5添入9,數組——9;
輸入——4,由於4<9直接添入,數組——9,4;
輸入——7,由於7>4同時7<9因此刪去4添入7,數組——9,7;
輸入——8,由於8>4同時8<9因此刪去7添入8,數組——9,8;
輸入——6,由於6<8直接添入,數組——9,8,6。

總的來說,它的本質就是當你在插入一個值時,應將在他之前存入的所有小於他的數值剔除,再將他存入數組中。

基礎就差不多就這樣ok辣~


良心推薦一些題:

單調隊列專題:

1.P1440 求m區間內的最小值 直通

思路:

  就是進行求解固定區間之內的最小值

上代碼:

技術分享
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e6 + 5;
int n,k,l,r;
int a[N],q[N],p[N];

int main() {
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]<=a[q[r]]) r--; //單調遞增 
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    printf("\n");
    memset(p,0,sizeof(p));
    l=0,r=0;
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]>=a[q[r]]) r--; //單調遞減 
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    return 0;
}
View Code

2.luogu P1886 滑動窗口 直通

思路:

  單調隊列的裸題,練手題,必刷!

  而且不僅有求最大值還有求最小值哦!

坑點:

  看清楚輸出的是什麽!

上代碼:

技術分享
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e6 + 5;
int n,k,l,r;
int a[N],q[N],p[N];

int main() {
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]<=a[q[r]]) r--; //單調遞增 
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    printf("\n");
    memset(p,0,sizeof(p));
    l=0,r=0;
    for(int i=1; i<=n; i++) {
        if(q[l]<=i-k) l++;
        while(l<=r && a[i]>=a[q[r]]) r--; //單調遞減 
        q[++r]=i;
        p[i]=a[q[l]];
    }
    for(int i=k; i<=n; i++) printf("%d ",p[i]);
    return 0;
}
View Code

3.luogu P1714 切蛋糕 直通

思路:

  因為求的是幸運總和最大

  總和?

  當然就是求一下前綴和辣~

  所以我們首先求一個前綴和,然後維護一個單調遞增的前綴和,最後更新答案時則使用max(ans,sum[i]-sum[q[l]])就結束辣!

坑點:

  註意這裏ans的初始化是什麽,我認為應該初始化為-99999999什麽的,不過初始化為0也是可以的!

  因為擁有ans=max(ans,sum[i]-sum[q[l]])這一步,所以如果當前的i==q[l]那麽sum[i]-sum[q[l]]一定為0,所以無論你初始化ans多麽小,ans最小都只能是0,所以需要把ans輸出化為0.

上代碼:

技術分享
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 500005;
int n,m,ans,l,r;
int sum[N],q[N];

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) {
        scanf("%d",&sum[i]);
        sum[i]+=sum[i-1];
        if(q[l]<i-m) l++;
        while(l<=r && sum[q[r]]>sum[i]) r--; //維護一個單調增區間 
        q[++r]=i;
        ans=max(ans,sum[i]-sum[q[l]]);
    }
    printf("%d",ans);
    return 0;
}
View Code

4.P1638 逛畫展 直通

思路:

  我們需要用桶來維護一下單調隊列,記錄一下每個畫家的作品出現的次數,如果隊頭的元素跟後面的元素有重復,那麽就刪除掉這個元素,以此類推

  然後最後的更新的話,必須保證所有顏色均出現過了,還需要記錄一個last,表示上一次更新的區間的長度是多少。

  如果現在的區間長度比last要小,並且所有顏色均出現,那麽就更新a,b端點坐標以及last,然後繼續重復即可

上代碼:

技術分享
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 1000001;
const int M = 2001;
int n,m,last=1<<30,a,b;
int x[N],c[M];

int main() {
    scanf("%d%d",&n,&m);
    for(int r=1,l=1,now; r<=n; r++) {
        scanf("%d",&x[r]);
        if(c[x[r]]==0) m--;
        c[x[r]]++;
        while(l<r && c[x[l]]>1) {
            c[x[l]]--;
            l++;
        }
        now=r-l+1;
        if(m==0 && now<last) last=now,a=l,b=r;
    }
    printf("%d %d",a,b);
    return 0;
}
View Code

額...至於單調棧什麽的,我大概就做過一個題qwq,就光貼上地址吧

直通

ok,先暫時說這麽多,以後我如果做到類似的題目還會發啦~

單調隊列,單調棧相關