这是一道最最基础的使用双端队列优化的题目。题目的意思就是求出指定长度子序列的最大值和最小值。
如果说硬要弄一个方程的话,那就是f[i]=max/min(a[j]) (f[i]指以i结尾的子序列,a[j]指原序列中第k个元素。i-k+1<=j<=i)
显然我们可以通过单调队列来维护最大值和最小值,复杂度O(n)
按理说双端队列应该有两个域,一个存下标,一个存关键值,但是这道题中关键值很容易从原序列中取出,所以就省掉了这个域,在这里提醒一下刚接触单调队列的人。
当然,这个数据范围暴力RMQ也可以过,其实也不是很暴力啦,但是跟O(n)的算法比就不太优美了。
提示:在POJ上做这道题的时候,如果你超时了,或者几千ms水过,建议你在两个方面进行改进。
第一,不要使用STL。事实上,由于自身容器类型的限制,deque在时间空间上都是非常不适用于OI题目的。而自己写队列速度快,空间小,而且代码也短。
第二,参考一下我的代码中读入输出的部分(这个就是所谓蛋疼IO优化)。POJ上很多题目卡scanf和printf,在G++下光读入都可能会超时。遇到这种情况换C++编译好了。
//By YY_More #include<cstdio> char c; int i,n,k,f[1000050],L,R,H,x,D[1000050]; inline int getmin(){ while (D[L]<=i-k) L++; return f[D[L]]; }; inline void insertmin(){ while (L<=R&&f[D[R]]>=f[i]) R--; D[++R]=i; }; inline int getmax(){ while (D[L]<=i-k) L++; return f[D[L]]; }; inline void insertmax(){ while (L<=R&&f[D[R]]<=f[i]) R--; D[++R]=i; }; inline void put(int x){ if(x< 0){ putchar('-'); x = -x; } if(x == 0){ putchar('0'); return; } char s[20]; int bas = 0; for(;x;x/=10)s[bas++] = x%10+'0'; for(;bas--;)putchar(s[bas]); return; } int main(){ while (true){ c=getchar(); if (c==' ') break; n=n*10+c-48; } while (true){ c=getchar(); if (c=='\n') break; k=k*10+c-48; } int y=1; while (true){ c=getchar(); if (c=='\n') {f[++H]=x*y;break;} if (c==' ') {f[++H]=x*y;y=1;x=0;}; if (c=='-') y=-1; if (c>='0'&&c<='9') x=x*10+c-48; } L=0;R=-1; for (i=1;i<=n;i++){ insertmin(); if (i>=k) { put(getmin()); if (i<n) putchar(' '); } } putchar('\n'); L=0;R=-1; for (i=1;i<=n;i++){ insertmax(); if (i>=k) { put(getmax()); if (i<n) putchar(' '); } } putchar('\n'); return 0; }