Subsequence 单调队列

题意:有一系列整数。 您的任务是找到满足以下条件的最长子序列: n, m and k. 子序列的最大元素和最小元素之间的差值不小于m且不大于k。

思路:我们可以想到队列这种结构,要保证子序列最长,我们就要保存最大与最小元素的值。同时在每次新增加元素后,如果该队列的最大值减去最小值不符合条件我们就要尽可能的剔除下标最小的那个

所以这里就是用了双端队列模拟    单调队列 来维护。用一个递增与一个递减的单调队列,我们就能存储到第i个下标之前符合序列的最大与最小值。

单调队列的维护步骤:
1.保证两个队列单调(否则pop队尾)
2.将新的元素插入到队列尾部
3.确定队列的最大最小差值符合条件,否则弹出下标最小的队首
3.如果符合条件就更新最大长度

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+9; 
struct node
{
    int x,num;//值与下标 
};
int p[maxn];
int main()
{
    int n,m,r;
    while(~scanf("%d%d%d",&n,&m,&r))
    {
        int pos=0;
        int ans=0;      //pos为右端点为i时符合条件的最优左端点
        for(int i=1; i<=n; i++)scanf("%d",&p[i]);
        deque<node>q1;  //递减
        deque<node>q2;  //递增
        for(int i=1; i<=n; i++)
        {
            while(!q1.empty()&&q1.back().x<p[i]) q1.pop_back(); 
                 //递减
                 node k ; k.x = p[i],k.num = i; 
                q1.push_back(k);
            while(!q2.empty()&&q2.back().x>p[i]) q2.pop_back();    
              //递增
                  k.x = p[i],k.num = i; 
                q2.push_back(k);
            while(!q1.empty()&&!q2.empty()&&q1.front().x-q2.front().x>r) 
            //如果当前队首元素差值,即最大最小值差大于r 则应令最大元素出队或最小元素出队 取决于谁的下标更小
            {
                if(q1.front().num<q2.front().num)
                    pos=q1.front().num,q1.pop_front();      
                else
                    pos=q2.front().num,q2.pop_front();
            }
            //pos会更新到最优左端点
            if(!q1.empty()&&!q2.empty()&&q1.front().x-q2.front().x>=m)//符合条件(就会更新长度) 
            {
                ans=max(ans,i-pos);
            }
            //记录答案
        }
        printf("%d
",ans);
    }
}
原文地址:https://www.cnblogs.com/Tianwell/p/11266098.html