单调队列 (POJ2823)

前几天做过这题...当时使用线段树做的....要跑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 :

  1. /*
  2. POJ2823 - 单调队列
  3. */
  4. #include<iostream>
  5. using namespace std;
  6. int n,m,a[1000001],Myqueue[1000001];
  7. void MinQueue()
  8. {
  9. int p=0,h=1,k,i;
  10. Myqueue[1]=1;
  11. for (k=1;k<=n;k++)
  12. {
  13. if (k-Myqueue[h]==m) h++;
  14. if (p==h-1 || a[k]>a[Myqueue[p]]){ p++; Myqueue[p]=k; }
  15. else
  16. {
  17. while (p>=h && a[k]<=a[Myqueue[p]]) { Myqueue[p]=k; p--; }
  18. p++;
  19. }
  20. if (k>=m) printf("%d ",a[Myqueue[h]]);
  21. }
  22. printf("\n");
  23. }
  24. void MaxQueue()
  25. {
  26. int p=0,h=1,k,i; Myqueue[1]=1;
  27. for (k=1;k<=n;k++)
  28. {
  29. if (k-Myqueue[h]==m) h++;
  30. if (p==h-1 || a[k]<a[Myqueue[p]]){ p++; Myqueue[p]=k; }
  31. else
  32. {
  33. while (p>=h && a[k]>=a[Myqueue[p]]) { Myqueue[p]=k; p--; }
  34. p++;
  35. }
  36. if (k>=m) printf("%d ",a[Myqueue[h]]);
  37. }
  38. printf("\n");
  39. }
  40. int main()
  41. {
  42. freopen("2823.in","r",stdin);
  43. freopen("2823T.out","w",stdout);
  44. scanf("%d%d",&n,&m);
  45. memset(a,0,sizeof(a));
  46. for (int i=1;i<=n;i++) scanf("%d",&a[i]);
  47. MinQueue();
  48. MaxQueue();
  49. return 0;
  50. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值