首先应该是说一下单调队列的定义
百科上是这样说的
单调队列
单调队列,即单调递减或单调递增的队列。使用频率不高,但在有些程序中会有非同寻常的作用。
从字面上理解就是一种队列按照递增或者递减的循序排列,最近做数据结构的时候,做了几道单调队列的题,在这里总结一下,
总结的是比较入门的单调队列,不过,单调队列用在更深层的层次应该是在dp优化的层面上。
我们首先看一下下面这个例子:
hdu 3530 Subsequence
题目的意思就是求给定的一个序列(我们设为A[n]),然后找一个子序列的最大值-最小值的值在区间[n , k]之间。求最长的长度是多少 ?
ps :序列长度范围[1, 100000],每个序列的值在[0, 1000000]之间
乍一看不知道应该怎么下手,觉得暴力做此题的话,复杂度O(n^2) 再加上样例 在评测姬不出问题的情况下,应该判TLE了
所以,这里就要用到单调队列这个数据结构了,
首先说一下单调队列的复杂度,是每个数出队入队各一次,复杂度O(n);
下面我们就开始对此题的解法。
我们可以设想,如果有一个,既有标记着位置,又有标记着值得从大到小得队列,和一个从小到大的队列,我们直接比较队列的头部,然后进行出对和入队,这不就可以解决了吗
例如给出一个样例 5 0 0 A[1-n] = {5 , 4 , 2 , 1 , 3}
max单调队列 min单调队列
i==1时 5 5 此时满足答案, ans =1;
i==2时 5 4(5出4入) 此时由于5 - 4>区间右值0 因此 max队列的头部出列
i==3时 4 2 (4出2入) 此时由于4-2>区间右值0 因此max 队列的头部出列
i==4时 2 1(2出1入) 此时由于2-1>区间右值0 因此max 队列的头部出列
i==5时 3 1 此时由于3-1>区间右值0 因此max 队列的头部出列
然后最后得出的结果是 ans = 1; 输出1;
ps:此题有一个坑 就是没有说区间的m 和 K的相对大小,如果K<m 则此区间不存在 因此输出为 0了;
代码如下(用了STL的deque,感觉这个写单调队列比较直观的可以理解)
#include <iostream>
#include <queue>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e5+7;
int a[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m,k;
while(cin>>n>>m>>k)
{
for(int i=1;i<=n;i++) cin>>a[i];
if(k<m){cout<<0<<endl;continue;}
deque<int>maxdq;
maxdq.clear();
deque<int>mindq;
mindq.clear();
int now=0;
int ans =-1;
for(int i=1;i<=n;i++)
{
while(!maxdq.empty()&&a[maxdq.back()]<a[i]) maxdq.pop_back();
maxdq.push_back(i);
while(!mindq.empty()&&a[mindq.back()]>a[i]) mindq.pop_back();
mindq.push_back(i);
//cout<<maxdq.front()<<' '<<mindq.front()<<endl;
//cout<<a[maxdq.front()]<<' '<<a[mindq.front()]<<endl;
while(!maxdq.empty()&&!mindq.empty()&&a[maxdq.front()]-a[mindq.front()]>k)
{
if(maxdq.front()<mindq.front())
{
now = maxdq.front();
maxdq.pop_front();
}
else{
now = mindq.front();
mindq.pop_front();
}
}
if(!maxdq.empty()&&!mindq.empty()&&a[maxdq.front()]-a[mindq.front()]>=m)
{
ans = max(ans,i-now);
}
}
cout<<ans<<endl;
}
return 0;
}