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<k,判斷max-min>=m是否滿足,滿足則更新答案。附上AC代碼如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
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]<a[tail])//維護遞減
                t2--;
            down[++t2]=a[tail];id2[t2]=tail;//記錄下標

            //維護隊首,滿足m<=max-min<=k
            while(down[h2]-up[h1]>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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章