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),而且常数也是十分优秀的
当然像这种求区间最值的问题也有一种简单粗暴的方法:线段树
代码如下:(这里代码就折叠掉了,有需求的读者可以自己阅读,就是求区间的最大值和最小值,连修改都不用,可以说是线段树的模板了)
View Code// 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; }