Subsequence(单调队列)

题意:给定一串数组,问序列中满足k>=最大值-最小值>=m的最长连续子序列的长度

个人觉得题面有点没说请(还是我的翻译的锅?),我做题的时候认为所求的是子序列(不连续),然后就卡了快一天去看题解,然后题解都是连续子序列(喵喵喵?),瞬间有点炸裂。

回归正题,题目要求的是最大值和最小值的差值满足条件的区间最长为多少,所以我们只需要关心所选区间的最大值和最小值,然而我们不可能每次都去询问区间的最大值和最小值,所以区间也是不可能去暴力遍历的,所以我们这样选择区间:以当前位置为最右端,我们只需要确定左端点的位置,那左端点该如何选择才能使得满足条件的区间最长呢?首先,区间内要满足k>=最大值-最小值>=m,所谓最左端不就是再往前一就不符合条件了吗?那如何不满足条件呢,即最大值-最小值>k或最大值-最小值<m。

先来讨论最大值-最小值>k的情况,此情况下可由我们符合条件的区间左端点向前移动一位获得,也就是说左端点前面位置的数大于现区间的最大值或小于现区间的最小值。
当最大值-最小值<m时,易证明如果现区间符合要求,那么此情况是不可能出现的,因为区间前面的数小于或等于当前区间的最大值且大于或等于当前区间的最小值。

那我们再来总结一下求解方法吧:由最大值-最小值<m情况下知此情形无法转移到符合条件的区间,所以我们需要特判此条件,我们真正要求解的过程是由最大值-最小值>k的情况过度到符合条件的区间,由上面的分析有此情况下左端点前一位置的数大于符合区间得最大值或者是小于当前区间的最小值,所以我们可以想到用一个单调递增队列和一个单调递减队列来解决,当最大值-最小值>k时,我们需要使最小值增大或将最大值减小,那如何选择呢?所求区间要最长,右端点固定,所以我们要使左端点尽可能的小,所以我们应该将左端点设置在min(最小值所在为值,最大值所在位置)+1处。
完(๑′ᴗ‵๑)

AC代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<deque>

using namespace std;

typedef long long ll;

deque<int> up;
deque<int> down;

int arr[1000000];

int main() {
    int n, m, k;
    while (~scanf("%d %d %d", &n, &m, &k)) {
        int ans = 0,top=0;
        up.clear();
        down.clear();
        for (int i = 1; i <= n; i++) {
            scanf("%d", &arr[i]);
        }
        for (int i = 1; i <= n; i++) {
            while (!up.empty() && arr[up.back()] >= arr[i]) {//递增
                up.pop_back();
            }
            up.push_back(i);
            while (!down.empty() && arr[down.back()] <= arr[i]) {//递减
                down.pop_back();
            }
            down.push_back(i);
            while (!up.empty() && !down.empty() && arr[down.front()] - arr[up.front()] > k) {
                if (up.front() < down.front()) {
                    top = up.front();
                    up.pop_front();
                }
                else {
                    top = down.front();
                    down.pop_front();
                }
            }
            if (!down.empty() && !up.empty() && arr[down.front()] - arr[up.front()] >= m) {//判断
                ans = max(ans, i - top);
            }
        }
        printf("%d\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值