hdu3530 Subsequence 单调队列

来源:互联网 发布:.group域名为什么不火 编辑:程序博客网 时间:2024/05/22 03:05

原题地址:

http://acm.hdu.edu.cn/showproblem.php?pid=3530


题意:求最长子序列,使序列中最大值和最小值的差不小于m不超过n。

思路:

①head和tail分别表示当前所维护的区间首位;

②维护两个单调序列,序列内需要保存的是数的位置,单调递增(似乎这道题严格不严格都可以)队列up[i],其队首为从head到tail的最小值,单调递减队列down[i],其队首为从head到tail的最小值。

③从1开始枚举tail,每次tail进入区间时,首先更新两个单调序列,此时可更新从head到tail的最大最小值。

④如果max-min>k,则说明需要更新head,使max减小或min增大,使head++,如果出现haed越过当前的最大值或者最小值,则更新最大值或最小值,直到max-min<k。

⑤此时,如果max-min>=m的条件也满足的话,则可更新答案tail-head+1。


注意容易出错:

维护单调序列的时候,应该是以已经更新的队首为首,一开始写成了总是以0为首。


感想:昨晚用贪心写了一遍,写了100多行太乱了各种错误,晚上又睡着思考了一番,思路一下子就清晰了。单调队列这种东西真是需要多练练啊。


代码:

#include "stdio.h"#include "string.h"#include "iostream"using namespace std;int up[100010],hdown,hup,down[100010],nup,ndown,ans,num[100010];int n,m,k;void get_up(int i)                    //更新单调递增区间{while(1){if(num[i]>num[up[nup]]||nup<hup){up[++nup]=i;break;}elsenup--;}}void get_down(int i)                  //更新单调递减区间{while(1){if(num[i]<num[down[ndown]]||ndown<hdown){down[++ndown]=i;break;}elsendown--;}}void init()                          //初始化以及输入{up[0]=0;down[0]=0;nup=0;hup=1;hdown=1;ndown=0;ans=0;for(int i=1;i<=n;i++)scanf("%d",&num[i]);}int main(){while(~scanf("%d%d%d",&n,&m,&k)){init();int head=1;for(int tail=1;tail<=n;tail++){get_up(tail);get_down(tail);while(num[down[hdown]]-num[up[hup]]>k){if(head==up[hup])                      //head更新到最大值或者最小值,需要改变最大或最小值hup++;if(head==down[hdown])hdown++;head++;                                }if(num[down[hdown]]-num[up[hup]]>=m)ans=max(ans,tail-head+1);}printf("%d\n",ans);}return 0;}


0 0
原创粉丝点击