Tip:还有很多更有深度的题目,这里不再给出,只给了几道基本的题目(本来想继续更的,但是现在做的题目不是这一块内容,以后有空可能会继续补上)

 

单调队列——看起来就是很高级的玩意儿,显然是个队列,而且其中的元素还具有单调性

当然,它不只只是一个简单的队列,还是一个双端队列,即队首队尾都可以弹出元素,当然可以用C++自带的STL<deque>实现,当然这篇博客里不建议使用这种写法,因为不开O2的话就会有一个大弊端——

 

单调队列裸题:滑动窗口

  线性的求一个区间内的最值,我们先来找个规律,比如这组数据:

 

  8 3
  1 3 -1 -3 5 3 6 7

 (以求最大值为例子)

  我们发现前两个数中 1  3   ,3的优先级明显大于1,原因?3比1大,而且3还在1的右边(这样在后面的更新中3还能起到作用,而1显然已经没有作用了,那么我们就可以把1从队列里面弹出去了!)当然,这个操作在3丢到队列里面的时候就可以进行了,同时在这个操作之前,我们还要把前面的元素和现在位置差距大于k的元素弹掉

  这样我们便可以保证队列中的元素是单调递增的,那么我们每次在n中取出k个元素的时候,只要把当前队列中的第k个元素放到输出列表中就好了!

  当然最小值也是一样的,维护一个单调递减的序列,在这个过程中,每次输出其中最小数

代码如下:(先求最小值,后求最大值)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 inline int read(){
 7     int ans=0,f=1; char chr=getchar();
 8     while(!isdigit(chr)){if(chr=='-') f=-1;chr=getchar();}
 9     while(isdigit(chr)) {ans=(ans<<3)+chr-48;chr=getchar();}
10     return ans*f;
11 }
12 void write(int x){
13     if(x<0) x=-x,putchar('-');
14     if(x>9) write(x/10);
15     putchar(x%10+48);
16 }
17 int q[1000005],h,t,n,a[1000005],k;
18 int main(){
19     n=read();k=read();
20     for(register int i=1;i<=n;++i) a[i]=read();
21     h=1,t=0;
22     for(register int i=1;i<=n;++i){//Min
23         while(h<=t&&q[h]+k<=i) ++h;
24         while(h<=t&&a[i]<=a[q[t]]) --t;
25         q[++t]=i;
26         if(i>=k) write(a[q[h]]),putchar(' ');
27     }puts("");
28     h=1,t=0;
29     for(register int i=1;i<=n;++i){//Max
30         while(h<=t&&q[h]+k<=i) ++h;
31         while(h<=t&&a[i]>=a[q[t]]) --t;
32         q[++t]=i;
33         if(i>=k) write(a[q[h]]),putchar(' ');
34     }
35     return 0;
36 }

 

【时间复杂度分析】

   显然外循环的复杂度为n,关键在于其中的while循环,分析一下可以知道,每一个元素在其中只会进入队列一次,出队列一次,所以总的时间复杂度为O(n),而且常数也是十分优秀的

     当然像这种求区间最值的问题也有一种简单粗暴的方法:线段树

代码如下:(这里代码就折叠掉了,有需求的读者可以自己阅读,就是求区间的最大值和最小值,连修改都不用,可以说是线段树的模板了)

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
#define ll long long
#define lson i << 1,l,m
#define rson i << 1| 1,m + 1,r
#define MAXN (int)1e6 + 5
using namespace std;

inline ll read(){
    char chr=getchar();
    ll f=1,ans=0;
    while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
    while(isdigit(chr))  {ans=ans*10;ans+=chr-'0';chr=getchar();}
    return ans*f;

}

void write(ll x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x<9)
        putchar(x+'0');
    else
        write(x/10),putchar(x%10+48);
}

struct P{
    ll l,r;
    ll max,add,min;
    ll mid(){
        return l + r >> 1;
    }
}t[MAXN << 2];
ll a[MAXN << 2];

void build(ll i,ll l,ll r){
    t[i].l = l;t[i].r = r;
    if(l == r){
        t[i].min = a[l];
        t[i].max = a[l];
        return;
    }
    ll m = t[i].mid();
    build(lson);
    build(rson);
    t[i].max =max( t[i << 1].max , t[i << 1 | 1].max );
    t[i].min =min( t[i << 1].min , t[i << 1 | 1].min );
}


ll qmin(ll i,ll l,ll r){
    if(l <= t[i].l && t[i].r <= r) return t[i].min;
    ll pp = 0x3f3f3f3f,qq = 0x3f3f3f3f;
    ll m = t[i].mid();
    if(l <= m) pp = qmin(i << 1,l,r);
    if(r > m)  qq = qmin(i << 1 | 1,l,r);
    return min(qq , pp);
}

ll qmax(ll i,ll l,ll r){
    if(l <= t[i].l && t[i].r <= r) return t[i].max;
    ll pp = -0x3f3f3f3f,qq = -0x3f3f3f3f;
    ll m = t[i].mid();
    if(l <= m) pp = qmax(i << 1,l,r);
    if(r > m)  qq = qmax(i << 1 | 1,l,r);
    return max(qq , pp);
}

ll n,m;
int main(){
    n = read() ;
    m = read() ;
    for(ll i = 1;i <= n;i ++)
        a[i] = read();
    build(1,1,n);
    for(int i = 1;i + m - 1 <= n;++i){
        printf("%lld",qmin(1,i,i+m-1));
        putchar(' ');
    }
    puts("");
    for(int i = 1;i + m - 1 <= n;++i){
        printf("%lld",qmax(1,i,i+m-1));
        putchar(' ');
    }
    return 0;
}
View Code

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-08-23
  • 2021-12-30
  • 2021-08-17
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-12-23
  • 2022-12-23
  • 2022-02-26
  • 2022-12-23
  • 2022-02-15
  • 2022-12-23
相关资源
相似解决方案