bzoj2096 pilots

Description
Tz又耍畸形了!!他要当飞行员,他拿到了一个飞行员测试难度序列,他设定了一个难度差的最大值,在序列中他想找到一个最长的子串,任意两个难度差不会超过他设定的最大值。耍畸形一个人是不行的,于是他找到了你。
Input
输入:第一行两个有空格隔开的整数k(0<=k<=2000,000,000),n(1<=n<=3000,000),k代表Tz设定的最大值,n代表难度序列的长度。第二行为n个由空格隔开的整数ai(1<=ai<=2000,000,000),表示难度序列。
Output
输出:最大的字串长度。

Sample Input
3 9
5 1 3 5 8 6 6 9 10

Sample Output
4
(有两个子串的长度为4: 5, 8, 6, 6 和8, 6, 6, 9.最长子串的长度就是4)


题解:
判断区间内的每一对值之差<k,则相当于区间的最大值最小值之差<k。很快的能想到,维护一个单调递增序列q1[]和一个单调递减序列q2[],定义这个区间的最大最小值,如果>k,则找队列中的下一个,相减与k作比较。个人认为最复杂的是区间的范围。代码中t是区间左端点,开始t=1,因此从第一个数开始。维护一个单调递减序列,一个递增,当两个队首差>k。那么在此之前区间最大最小之差<=k的情况区间从t=1到当前位置长度已经到达最大值。此时,只能去改变左端点,左端点要改变到哪里呢?肯定是两个队首对应的位置较小的一个的下一个,从这里算作新区间的左端点。
例如:
3 4 5 1 4 6
i到达5时,两个队列为:
q1[]={5}的下标(里面应该是5的下标,方便看)
q2[]={3 4 5}的下标
ans=3(i)-1(t)+1=3;

这时再进一个1
那么
q1[]={5 1}的下标
q2[]={1}的下标
队首大于3
执行while(),q1[]={1}的下标,直到不大于k,这时t=1的下标
q1[]={1}
q2[]={1}
那么ans=max(3,4-4+1)=3;

加入4,
q1[]={4};
q2[]={1 4};
ans=max(3,2)=3

加入6
q1[]={6}
q2[]={1 4 6}
6-1>k
while(),6-3<=k
ans=max(3,6(i)-5(t)+1)=3;

因此,最终连续的是,3 4 5最长。

因此,最难想的是如何去定义这个区间的左右端点,一旦q1首-q2首>k的时候,要把这两个值分开,因此找最小的下一个为新区间的左端点。至于为什么不改变右端点,因为此刻能到达的右端点就是最长区间了,如果要往左移动,只会使区间变得更短。

#include<iostream>
#include<cstdio>
using namespace std;
int ans=1,k,n,a[1000005],q1[1000005],q2[1000005]; 
void work()
{
    int l1=1,l2=1,r1=0,r2=0,t=1;//一开始没有赋值,导致错误
    for(int i=1;i<=n;i++)
    {
        while(l1<=r1 && a[i]>=a[q1[r1]]) r1--;//维护一个单调递减的序列 
        while(l2<=r2 && a[i]<=a[q2[r2]]) r2--;//维护一个单调递增的序列 
        q1[++r1]=i;q2[++r2]=i;

        while(a[q1[l1]]-a[q2[l2]]>k)
        {
            if(q1[l1]<q2[l2]) t=q1[l1]+1,l1++;
            else t=q2[l2]+1,l2++; 
        }
//t是什么?用来记录子串的开头下标。看q1[l1]和q2[l2]的最小值,然后把最小值+1; 

        ans=max(ans,i-t+1);
//只要加入一个数,都会计算ans,一开始理解成了上一步的while运算之后才计算ans.
    }
}
int main()
{
    cin>>k>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    work();
    cout<<ans<<endl;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值