题目地址 http://acm.hdu.edu.cn/showproblem.php?pid=3530
题目大意: 给定一个数列,长度n(1<=n<=100000), 值m和k, 求最长子序列,满足当中的最大值-最小值差不小于m且不大于k。
与前面介绍的题目不太一样,这道题反过来了,没有规定窗长度,相反是要我们求要求条件的最大的窗。
道理还是一样,当窗长规定为l时,我们只需要保存i的前l-1个数当中的最大值和最小值,而要我们求窗长时,我们可以自己设定窗的开始位置start,同样保存从位置start到到当前i的这段数中的最大值单调队列和最小值单调队列。
假设queMax是最递减队列,queMin是递增队列,分别保存的是从start到i这段长为i-start+1的数的最大值和最小值,headMax, headMin 是他们队列的头索引。如果m<=queMax[headMax]-queMin[headMin]<=k,那这段长为i-k+1的序列就满足题目的要求。
假设我们start到i的序列满足要求,我们接下来当然是看加上第i+1个数后是否还满足要求。方法如下:
1.将第i+1个数插入到两个队列当中,然后看是否满足要求,如果满足要求,保存长度(i+1)-k+1。
2.否则,如果queMax[headMax]-queMin[headMin]<=m,不作任何处理;
如果queMax[headMax]-queMin[headMin]>=k,那么我们就要增加headMin或headMax,为了求最长序列,我们当然先择下标最小的那个头元素了。一直将headMin或headMax增加到满足条件。#include<cstdio> #define maxn 100010 struct Dq { int val,pos; }; Dq queMax[maxn],queMin[maxn],temp; int main() { int n,m,k,i,headMax,headMin,endMax,endMin,start,ans; while(scanf("%d%d%d",&n,&m,&k)!=EOF) { headMax=headMin=start=ans=0; endMax=endMin=-1; for(i=0;i<n;i++) { scanf("%d",&temp.val),temp.pos=i; while(headMax<=endMax && queMax[endMax].val<=temp.val) endMax--; queMax[++endMax]=temp; while(headMin<=endMin && queMin[endMin].val>=temp.val) endMin--; queMin[++endMin]=temp; while(queMax[headMax].val-queMin[headMin].val>k) { if(queMax[headMax].pos<queMin[headMin].pos) { ++headMax; start = queMax[headMax].pos; } else { ++headMin; start = queMin[headMin].pos; } } if(queMax[headMax].val-queMin[headMin].val<=k && queMax[headMax].val-queMin[headMin].val>=m) { if(i-start+1>ans) ans = i-start+1; } } printf("%d/n",ans); } return 0; }