前几天做过这题...当时使用线段树做的....要跑9500MS才能过...今天看了下单调队列...以为很难...但理解一下..发现单调队列其实很简单..
单调队列是从数列前扫到数列后...维护一个最值或者一个所需的最优解之类的...每次的最优解都是在队列的头....所以要一直维护队列..使其从头到尾都是单调的..要能保证如果当前头要出去了...后面的元素能马上顶上来作为头...
就拿POJ2823来举例....题目要求是给了一串n个数...从左到右每次框k个连续数..问每次框的数中最大数和最小数是什么..样例输入输出:
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7
就拿样例说....准备一个队列...Myqueue...h代表队列头( 里面存的是下标不是数..为了是判断队首出列)..p代表队列尾..初始值h=1,p=0...就拿每次要一段最小值的队列变化过程来描述一下:
1、插入第1个数 Myqueue = { 1 } h=1; p=1 小于所给的框..还不需输出
2、插入第2个数 Myqueue= { 1 2 } h=1; p=2 ( 因为2号元素比1号元素大...先排到后面 ) 小于所给的框..还不需输出
3、插入第3个数 Myqueue= { 3 } h=1; p=1; ( 因为3号元素为-1比1号元素的1和2号元素的3都要小~~所以一直挤到最前面...1,2都出列 ) 输出 a[ Myqueue[h] ]的值 -1
4 、插入第4个数 Myqueue={ 4 } h=1; p=1; ( 因为4号元素比3号元素小....所以挤掉3号元素 ) 输出 a[ Myqueue[h] ]的值 -3
5、插入第5个数 Myqueue={ 4 5 } h=1; p=2 ( 因为5号元素比4号元素大...先排到后面 ) 输出 a[ Myqueue[h] ]的值 -3
6、插入第6个数 Myqueue={4 6 } h=1; p=2 ( 因为6号元素比5号元素小....但是又没有4号元素小..所以只能挤掉5号元素 ) 输出 a[ Myqueue[h] ]的值 -3
7、插入第7个数 Myqueue={ 6 7 } h=2; p=3 { 这里为什么4出列了?因为4到7已经不能被所给的范围框住了...所以4要出列..然后继续操作...因为7号元素比6号元素大...先排到后面) 输出 a[ Myqueue[h] ]的值 3
8、插入第8个数 Myqueue={6 7 8 } h=2; p=4; { 因为8号元素比7号元素和6号元素都要大...所以放到后面... ) 输出 a[ Myqueue[h] ]的值 3
还有一点要特别注意!!!如果后面的数和h[p]元素的数相等时...也要挤掉h[p]的....因为这个我WA了一次...囧....
这道题用单调队列来跑只要5000MS..的确快了不少..但看大牛们还能更快...想知道怎么做到的...
PS:...有个地方一定要注意...每次插入新数到队列都要从队尾往队首扫..扫到比自己小的就确定位置..扫到比自大的就把这个大的数给挤掉.....如果从前往后扫....例如
1 2 3 4 5 6 7 8 9 ..... 很长一列...要插入一个很大的数...从前一直遍历到最后面..才确定位置..这不是关键..关键是这一路过去..什么操作都没有做..白白扫了一大片空间...而从后往前扫...要么确定位置...要么踢到元素...总是在操作..所以必须要从后往前来判断并插入....
Program :
/* POJ2823 - 单调队列 */ #include<iostream> using namespace std; int n,m,a[1000001],Myqueue[1000001]; void MinQueue() { int p=0,h=1,k,i; Myqueue[1]=1; for (k=1;k<=n;k++) { if (k-Myqueue[h]==m) h++; if (p==h-1 || a[k]>a[Myqueue[p]]){ p++; Myqueue[p]=k; } else { while (p>=h && a[k]<=a[Myqueue[p]]) { Myqueue[p]=k; p--; } p++; } if (k>=m) printf("%d ",a[Myqueue[h]]); } printf("\n"); } void MaxQueue() { int p=0,h=1,k,i; Myqueue[1]=1; for (k=1;k<=n;k++) { if (k-Myqueue[h]==m) h++; if (p==h-1 || a[k]<a[Myqueue[p]]){ p++; Myqueue[p]=k; } else { while (p>=h && a[k]>=a[Myqueue[p]]) { Myqueue[p]=k; p--; } p++; } if (k>=m) printf("%d ",a[Myqueue[h]]); } printf("\n"); } int main() { freopen("2823.in","r",stdin); freopen("2823T.out","w",stdout); scanf("%d%d",&n,&m); memset(a,0,sizeof(a)); for (int i=1;i<=n;i++) scanf("%d",&a[i]); MinQueue(); MaxQueue(); return 0; }