何为单调队列?
单调队列是一个队列(废话)
而且必须同时满足下标单调和值单调两个单调特性。
跟优先队列不同,优先队列直接使用堆(heap)来实现,如何删去特定下标元素?不明。
本人喜欢用单调队列存下标,这样比存值不知道高到哪里去了。
新来一个元素,进队。之后特判长度有没有超过。超过则把最前面的元素出队。
之后,如果不满足性质,就把前面的元素顶掉,直到满足性质为止。
然后才可以把队首元素拿来用。
有几个很坑的地方,具体看代码注释。
(为什么top是队尾而tail是队首?laughcry)
例题:洛谷P1886,高级模板题。还有一道剧毒的P2216。(排版很奇怪,都怪反人类的博客园☺)
AC代码:
1 #include <cstdio> 2 const int N = 1000010; 3 4 int pl[N], l_t, l_h = 1, ansl[N]; 5 int ps[N], s_t, s_h = 1, anss[N]; 6 int a[N]; 7 8 int main() { 9 int n, k; 10 scanf("%d%d", &n, &k); 11 for(int i = 1; i <= n; i++) { 12 scanf("%d", &a[i]); 13 } 14 for(int i = 1; i <= n; i++) { 15 pl[++l_t] = i; 16 ps[++s_t] = i; 17 if(pl[l_h] + k <= i) { 18 l_h++; 19 } 20 if(ps[s_h] + k <= i) { 21 s_h++; 22 } 23 while(l_t > l_h && a[pl[l_t - 1]] <= a[i]) { 24 l_t--; 25 } 26 while(s_t > s_h && a[ps[s_t - 1]] >= a[i]) { 27 s_t--; 28 } 29 pl[l_t] = i; 30 ps[s_t] = i; 31 ansl[i] = pl[l_h]; 32 anss[i] = ps[s_h]; 33 } 34 for(int i = k; i <= n; i++) { 35 printf("%d ", a[anss[i]]); 36 } 37 printf(" "); 38 for(int i = k; i <= n; i++) { 39 printf("%d ", a[ansl[i]]); 40 } 41 return 0; 42 }
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 const int N=1002; 5 struct ah{int large,small;}f[N][N]; 6 int c[N][N]; 7 int p1[N],p2[N],top1,tail1=1,top2,tail2=1;///存下标 8 int a,b,n;///p1求min,p2求max 9 long long int ans=999999999,s; 10 11 int main() 12 { 13 scanf ("%d%d%d",&a,&b,&n); 14 for(int i=1;i<=a;i++) 15 { 16 top1=top2=0; 17 tail1=tail2=1; 18 for(int j=1;j<=b;j++) 19 { 20 scanf ("%d",&c[i][j]); 21 p1[++top1]=p2[++top2]=j; 22 while(c[i][p1[top1]]<=c[i][p1[top1-1]] && top1>tail1) 23 {///单调队列维护 24 p1[top1-1]=p1[top1]; 25 top1--; 26 } 27 while(c[i][p2[top2]]>=c[i][p2[top2-1]] && top2>tail2) 28 { 29 p2[top2-1]=p2[top2]; 30 top2--; 31 } 32 if(p1[top1]-p1[tail1]>=n) tail1++;///区间长度n 33 if(p2[top2]-p2[tail2]>=n) tail2++; 34 if(j>=n) 35 {///产生最大最小值 36 f[i][j-n+1].large=c[i][p2[tail2]]; 37 f[i][j-n+1].small=c[i][p1[tail1]]; 38 } 39 } 40 } 41 ///读入完毕,处理①完毕 42 /* 43 for(int i=1;i<=a;i++)///调试 44 { 45 for(int j=1;j<=b-n+1;j++) 46 { 47 printf("%d-%d ",f[i][j].large,f[i][j].small); 48 } 49 printf(" "); 50 }*/ 51 52 53 for(int j=1;j<=b-n+1;j++) 54 { 55 top1=top2=0; 56 tail1=tail2=1; 57 for(int i=1;i<=a;i++)///p1 small p2 large 58 { 59 p1[++top1]=p2[++top2]=i; 60 while(f[p1[top1]][j].small<=f[p1[top1-1]][j].small && top1>tail1) 61 { 62 p1[top1-1]=p1[top1]; 63 top1--; 64 } 65 while(f[p2[top2]][j].large>=f[p2[top2-1]][j].large && top2>tail2) 66 { 67 p2[top2-1]=p2[top2]; 68 top2--; 69 } 70 if(p1[top1]-p1[tail1]>=n) tail1++; 71 if(p2[top2]-p2[tail2]>=n) tail2++; 72 if(i>=n) 73 {///产生最值 74 s=1ll*(f[p2[tail2]][j].large-f[p1[tail1]][j].small); 75 //printf("%d ",s); 76 ans=min(ans,s); 77 } 78 79 } 80 //printf(" "); 81 } 82 printf("%lld",ans); 83 return 0; 84 }
博客园排版有剧毒。。。