单调队列

一、 什么是单调(双端)队列
单调队列,顾名思义,就是一个元素单调的队列,那么就能保证队首的元素是最小(最大)的,从而满足动态规划的最优性问题的需求。
单调队列,又名双端队列。双端队列,就是说它不同于一般的队列只能在队首删除、队尾插入,它能够在队首、队尾同时进行删除。
【单调队列的性质】
一般,在动态规划的过程中,单调队列中每个元素一般存储的是两个值:
1、在原数列中的位置(下标)
2、 他在动态规划中的状态值
而单调队列则保证这两个值同时单调。
从以上看,单调队列的元素最好用一个类来放,不这样的话,就要开两个数组。。。

单调队列:单调队列 即保持队列中的元素单调递增(或递减)的这样一个队列,可以从两头删除,只能从队尾插入。单调队列的具体作用在于,由于保持队列中的元素满足单调性,对手元素便是极小值(极大值)了。

http://poj.org/problem?id=2823

[cpp]  view plain copy
  1. //poj-2823--单调队列  
  2. #include<iostream>  
  3. #include<cstdio>  
  4. using namespace std;  
  5.   
  6. const int MAX = 1000001;  
  7. //两个单调队列  
  8. int dq1[MAX];    //一个存单调递增  
  9. int dq2[MAX];    //一个存单调递减  
  10. int a[MAX];  
  11. int main(void)  
  12. {  
  13.     int i,n,k,front1,front2,tail1,tail2,start,ans;  
  14.   
  15.     while(scanf("%d %d",&n,&k)!=EOF)  
  16.     {  
  17.         for(i = 0 ; i < n ; ++i)  
  18.         scanf("%d",&a[i]);  
  19.         front1 = 0, tail1 = -1;  
  20.         front2 = 0, tail2 = -1;  
  21.         ans = start = 0;  
  22.         for(i = 0 ; i < k ; ++i)  
  23.         {  
  24.             while(front1 <= tail1 && a[ dq1[tail1] ] <= a[i])   //当前元素大于单调递增队列的队尾元素的时候,队尾的元素依次弹出队列,直到队尾元素大于当前当前元素的时候,将当前元素插入队尾  
  25.                 --tail1;  
  26.             dq1[ ++tail1 ] = i;    //只需要记录下标即可  
  27.   
  28.             while(front2 <= tail2 && a[ dq2[tail2] ] >= a[i])   //当前元素小于单调递减队列的队尾元素的时候,队尾的元素依次弹出队列,直到队尾元素小于当前当前元素的时候,将当前元素插入队尾  
  29.                 --tail2;  
  30.             dq2[ ++tail2 ] = i;    //只需要记录下标即可  
  31.         }  
  32.         printf("%d ",a[ dq2[ front2 ] ]);  
  33.         for( ; i < n ; ++i)  
  34.         {  
  35.             while(front2 <= tail2 && a[ dq2[tail2] ] >= a[i])  
  36.                 --tail2;  
  37.             dq2[ ++tail2 ] = i;   
  38.             while(dq2[ front2 ] <= i - k)  
  39.                 ++front2;  
  40.             if(i != n-1)  
  41.                 printf("%d ",a[ dq2[ front2 ] ]);  
  42.         }  
  43.         printf("%d\n",a[ dq2[ front2 ] ]);  
  44.           
  45.         //输出最大值  
  46.         printf("%d ",a[ dq1[ front1 ] ]);  
  47.         for(i=k ; i < n ; ++i)  
  48.         {  
  49.             while(front1 <= tail1 && a[ dq1[tail1] ] <= a[i])  
  50.                 --tail1;  
  51.             dq1[ ++tail1 ] = i;   
  52.             while(dq1[ front1 ] <= i - k)  
  53.                 ++front1;  
  54.             if(i != n-1)  
  55.                 printf("%d ",a[ dq1[ front1 ] ]);  
  56.         }  
  57.         printf("%d\n",a[ dq1[ front1 ] ]);  
  58.     }  
  59.     return 0;  
  60. }  
http://acm.hdu.edu.cn/showproblem.php?pid=3530    Subsequence

[cpp]  view plain copy
  1. /* 
  2. 题意:给出一个序列,求最长的连续子序列,使得 M<=Max-Min<=K 
  3.        n <= 10^5 
  4. 依次枚举剩下的N-1个元素,并且将当前未入队的第一个元素和队尾元素比较,当且仅当队列为非空并且队尾元素的值小于当前未入队的元素时, 
  5. 将队尾元素删除(也就是队尾指针-1),因为当前的元素比队尾元素大,所以在区间内队尾元素不会是最大值了。 
  6. 重复这个过程直到队列空或者队尾元素比当前元素大, 
  7. */  
  8. #include<iostream>  
  9. #include<cstdio>  
  10. using namespace std;  
  11.   
  12. const int MAX = 100001;  
  13. //两个单调队列  
  14. int dq1[MAX];    //一个存单调递增  
  15. int dq2[MAX];    //一个存单调递减  
  16. int a[MAX];  
  17.   
  18. inline bool scan_d(int &num)  //  这个就是 加速的 关键了     
  19. {  
  20.     char in;bool IsN=false;  
  21.     in=getchar();  
  22.     if(in==EOF)  
  23.         return false;  
  24.     while(in!='-'&&(in<'0'||in>'9')) in=getchar();  
  25.     if(in=='-')   { IsN=true;num=0;}  
  26.     else num=in-'0';  
  27.     while(in=getchar(),in>='0'&&in<='9')  
  28.     {  
  29.         num*=10,num+=in-'0';  
  30.     }  
  31.     if(IsN)  
  32.         num=-num;  
  33.     return true;  
  34. }  
  35.   
  36. int main(void)  
  37. {  
  38.     int i,n,m,k,front1,front2,tail1,tail2,start,ans;  
  39.     while(scanf("%d %d %d",&n,&m,&k) != EOF)  
  40.     {  
  41.         for(i = 0 ; i < n ; ++i)  
  42.             scan_d(a[i]);  
  43.         front1 = 0, tail1 = -1;  
  44.         front2 = 0, tail2 = -1;  
  45.         ans = start = 0;  
  46.         for(i = 0 ; i < n ; ++i)  
  47.         {  
  48.             while(front1 <= tail1 && a[ dq1[tail1] ] <= a[i])   //当前元素大于单调递增队列的队尾元素的时候,队尾的元素依次弹出队列,直到队尾元素大于当前当前元素的时候,将当前元素插入队尾  
  49.                 --tail1;  
  50.             dq1[ ++tail1 ] = i;    //只需要记录下标即可  
  51.   
  52.             while(front2 <= tail2 && a[ dq2[tail2] ] >= a[i])   //当前元素小于单调递减队列的队尾元素的时候,队尾的元素依次弹出队列,直到队尾元素小于当前当前元素的时候,将当前元素插入队尾  
  53.                 --tail2;  
  54.             dq2[ ++tail2 ] = i;    //只需要记录下标即可  
  55.   
  56.             /* 
  57.             Max - Min 为两个队列的队首之差 
  58.             while(Max-Min>K)  看哪个的队首元素比较靠前,就把谁往后移动 
  59.             */  
  60.             while(a[ dq1[front1] ] - a[ dq2[front2] ] > k)  
  61.             {  
  62.                 if(dq1[front1] < dq2[front2] )  
  63.                 {  
  64.                     start = dq1[front1] + 1;  
  65.                     ++front1;  
  66.                 }  
  67.                 else  
  68.                 {  
  69.                     start = dq2[front2] + 1;  
  70.                     ++front2;  
  71.                 }  
  72.             }  
  73.             if(a[ dq1[front1] ] - a[ dq2[front2] ] >= m)  
  74.             {  
  75.                 if(i - start +1 > ans)  
  76.                     ans = i - start + 1;  
  77.             }  
  78.         }  
  79.         printf("%d\n",ans);  
  80.     }  
  81.     return 0;  
  82. }  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值