HDU3530 Subsequence 单调队列

先附上大佬博客Orz:https://blog.csdn.net/dan__ge/article/details/51746590

http://www.itkeyword.com/doc/7101227000454544193/hdu3530-Subsequence

找数列中最长的子序列,要求最大值减去最小值大于等于m小于等于k。这道题被归到了单调队列里,但真的没想到要用两个。。之前做过几道单调队列的题,实现原理大概就是维护队首并在队尾插入元素,保证队列的单调性。队首看题目要求,比如限制区间长度,本题在后边分析;而队尾每插入一个元素就要从后往前去除冗杂状态。

head和tail为维护区间的左右端点,构造两个单调队列:一个递增序列up[]和一个递减序列down[],它们都以tail为结尾,且队首元素的下标>head;这两个序列存的是元素值,所以另外再开两个数组记录元素值对应的下标(其实直接记录下标也可以)。tail从1到n遍历,每次都插入up和down队列末尾。难点在于队首的维护。可知在当前区间中,最大值是down的队首元素,最小值是up的队首元素,若max-min>k则需要令head++(使max减小或min增大);若head超过了up或down的队首元素下标,更新两数组的队首(其实就是队首指针++)。直到max-min=m是否满足,满足则更新答案。附上AC代码如下:

#include
#include
#include
#include
using namespace std;
const int INF=0x3f3f3f3f;
const int MAX=100005;
int n,m,k;
int a[MAX];

//递增序列
int up[MAX],id1[MAX];//记录数值,下标
int h1,t1;//队首,队尾
//递减序列
int down[MAX],id2[MAX];
int h2,t2;
void init()
{
    memset(up,0,sizeof(up));memset(id1,0,sizeof(id1));
    memset(down,0,sizeof(down));memset(id2,0,sizeof(id2));
    h1=1;t1=0;h2=1;t2=0;//注意此处初始化(与后边的"h1<=t1"对应)!
}

int main()
{
    while(scanf("%d%d%d",&n,&m,&k)==3)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        init();int ans=0;
        int head=1;
        for(int tail=1;tail<=n;tail++)
        {
            //队尾插入元素并去掉影响单调性的值
            while(h1<=t1&&up[t1]>a[tail])//维护递增
                t1--;
            up[++t1]=a[tail];id1[t1]=tail;//记录下标
            while(h2<=t2&&down[t2]k&&h1<=t1&&h2<=t2)
            {
                if(head==id1[h1])//head超过了队首元素下标
                    h1++;
                if(head==id2[h2])
                    h2++;
                head++;
            }
            if(down[h2]-up[h1]>=m)
                ans=max(ans,tail-head+1);//更新结果
        }
        printf("%d\n",ans);
    }
	return 0;
}

 

你可能感兴趣的:(算法-STL,数据结构)