单调栈和单调队列

理解单调栈和单单调队列之前,要明白一种技巧,叫做尺取法。

尺取法

尺取法,两个位置,一个是l,一个r,r一位位的左移,l根据条件左移。比如POJ 3061
求最大连续字串和不超过s
l r初始化为0,r左移,总和加上r位置的值,如果总和一旦大于s,l开始左移,直到满足[l,r]区间的总和小于s,这种通过l,r反复推进的方法,就叫尺取法。

细心的人可以发现,这种方法,求结果一定要满足,右边一个位置跨过rl一定是不合法的的情况。

单调栈

其实这个和尺取法关系不大。。。。
单调栈,用于求最左边(右边)的第一个满足某种具有单调性质条件*(比如大于,小于)*的位置。
距离,求第一个大于的位置。求大于,将不大于的数全部加入单调栈里面,保证栈单调递减,(下面距离栈中存的是小标,注意区分值和下标)
假设有1 3 5 2 1 4 7 6
一开始栈为空,将第一个数位置加进去,此时栈中有下标1
到第二个数 3 ,栈顶 位置的值 小于3弹出,赋值位置右边第一个大于他的数下标是2,然后栈为空,将2号位置加入栈,依次类推.
5 弹出下标2 加入下标3,到 2 因为栈顶位置的值大于2不弹出,直到4 弹出值2,1,不弹出5
当前值比栈顶的数大,弹出栈顶的值,并赋值,否则加入栈。

单调队列

单调队列和尺取法用点像,和单调栈也差距不大。总结来说,位置尺取,队列单调,就想尺取法和单调栈的结合体。例子 HDU 3530
按题目来讲: 查寻区间最大最小值之差在[m,k]之间的最大长度。
现按照尺取的方法来:不断移动,l,r,虽然从在r右边跨过rl 最大最小值之差绝对是大于等于[r,l]之间的,但是最大最小不可能一直是端点,可能[r,l]之间存在比r,l大或者小的值。那么该如何处理呢,单调栈可以保存一个单调的子序列,我们是否能和他一样保存[l,r],的单增子序列呢?很显然是可以的。因为我们的[l,r]有两端,所以用栈是肯定不行的,那么就只能用双端队列。我们建立两个双端队列,一个保存[l,r]递增的子序列,一个保存[l,r]递减的子序列,那么递减队列中第一个值保存的就是[l,r]区间的最大值,递增的就是最小值。那么假设[l,r] 区间的最大值和最小值不满足条件了,要尺取就要移动l,怎么移动?我们要改变最大最小值。改变哪里一个?肯定是离r最远的那一个,我们单调队列保存的下标,直接把l移动到两个队列队首,最小的下标+1,然后弹出队首,就相当于找到了新[l,r]最大(最小)值 (因为我们队列是单调的,弹出了最大(最小)值就相当于找到了次大(次小)值,在新区间就没有比他小的了 ) 然后,一直循环直到满足条件为止。
单增队列就是队尾进,每次和单调栈一样保证队列中是单调的,队首永远都是最小的或者最大的

HDU 3530 AC代码

#include "bits/stdc++.h"

using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> P;
#define VNAME(value) (#value)
#define bug printf("*********\n");
#define debug(x) cout<<"["<<VNAME(x)<<" = "<<x<<"]"<<endl;
#define mid (l + r) / 2
#define chl 2 * k + 1
#define chr 2 * k + 2
#define lson l, mid, chl
#define rson mid + 1, r, chr
#define pb push_back
#define mem(a, b) memset(a, b, sizeof(a));


const LL mod = (LL) 1e9 + 7;
const int maxn = (int) 1e6 + 5;
const int INF = 0x7fffffff;
const LL inf = 0x3f3f3f3f;
const double eps = 1e-8;
#ifndef ONLINE_JUDGE
clock_t prostart = clock();
#endif

void f() {
#ifndef ONLINE_JUDGE
    freopen("../data.in", "r", stdin);
#endif
}

struct deq {
    static const int start = maxn * 2;
    int dat[start * 2];
    int l = start;
    int r = start;

    void push_front(int x) {
        dat[l--] = x;
    }

    void pop_front() {
        l++;
    }

    int front() {
        return dat[l + 1];
    }

    void push_back(int x) {
        dat[++r] = x;
    }

    void pop_back() {
        r--;
    }

    int back() {
        return dat[r];
    }

    int size() {
        return r - l;
    }

    void clear() {
        r = l = start;
    }

    bool empty() {
        return r == l;
    }
};

deque<int> dq, dq2;


int n, m, k;
int a[maxn];

int main() {
    f();
    while (~scanf("%d%d%d", &n, &m, &k)) {
        int ans = 0;
        dq.clear();
        dq2.clear();
        int pos = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            // 大的弹出所以最前面是最小的
            while (dq.size() > 0 && a[dq.back()] > a[i]) {
                dq.pop_back();
            }

            dq.push_back(i);
            while (dq2.size() > 0 && a[dq2.back()] < a[i]) {
                dq2.pop_back();
            }
            dq2.push_back(i);
            while (a[dq2.front()] - a[dq.front()] > k) {
                if (dq2.front() < dq.front()) {
                    pos = dq2.front();
                    dq2.pop_front();

                } else {
                    pos = dq.front();
                    dq.pop_front();
                }
            }
            if (dq2.size() && dq.size() && a[dq2.front()] - a[dq.front()] >= m) {
                ans = max(ans, i - pos);
            }
        }
        printf("%d\n", ans);
    }
#ifndef ONLINE_JUDGE
    cout << "运行时间:" << 1.0 * (clock() - prostart) / CLOCKS_PER_SEC << "s\n";
#endif // ONLIN_JUDGE
    return 0;
}
/*
5
4
 */


友情提示:最好别用 std 里面的栈和队列,太慢了。
进阶题:2019牛客暑期多校训练营(第三场)F Planting Trees
题解 : 2019牛客暑期多校训练营(第三场)F Planting Trees

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值