HDOJ 3530 Subsequence(单调队列 + 经典应用)

题意:

给你一个长度为 n 的数列,要求一个子区间,使得区间的最大值与最小值的差 s 满足, m<=s<=k,求满足条件的最长子区间。

思路:

1. 首先要定义两个单调队列,一个为窗口范围内单调递减的最大值序列 decq,一个为窗口范围内单调递增最小值序列 incq 

2. 然后代码中定义了一个窗口大小 size,满足条件的 size 最大值即是本题所要输出的结果。

3. 此题中比较精巧的一点就是只有当超过上届也就是 s > k 时才会考虑减小窗口,因为随着 i 的推移,窗口里的最大/小值会存在改变,只会超过上届更大,不会比下届更低。

4. 随着 i 的向后推移,s 不断变化,一旦不满足题目条件,则要考虑从左向右缩小窗口,因为到 i 为止,此时的最优情况也就是 size 了。只有当改善左边界,才能改变最优值。

  // 62ms

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 100010;

int num[MAXN], incq[MAXN], decq[MAXN];

int main()
{
    int n, m, k;
    while (scanf("%d %d %d", &n, &m, &k) != EOF)
    {
        for (int i = 1; i <= n; ++i)
            scanf("%d", &num[i]);

        int si = 0, ei = -1;
        int sd = 0, ed = -1;
        int ans = 0, size = 0;

        for (int i = 1; i <= n; ++i)
        {
            while (si <= ei && num[i] < num[incq[ei]]) 
                --ei;
            incq[++ei] = i;

            while (sd <= ed && num[i] > num[decq[ed]]) 
                --ed;
            decq[++ed] = i;

            size += 1;

            while (size > 0)
            {
                if (num[decq[sd]] - num[incq[si]] > k)
                {
                    if (decq[sd] == i - size + 1) ++sd;
                    if (incq[si] == i - size + 1) ++si;
                    
                    size -= 1;
                }
                else break ;
            }

            if (num[decq[sd]] - num[incq[si]] >= m && ans < size)
                ans = size;
        }
        printf("%d\n", ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kedebug/p/2937215.html