第一个滑动窗口以外的单调队列题。。。
求一个数列中满足m <= max-min <= k的最长连续子序列的长度,n <= 1e5
思路:看到连续区间的最值,想到维护递增递减两个单调队列,max-min自然就是队首元素的差值
找到了合适的数据结构,接下来最主要的问题就是如何利用单调队列的性质避开枚举区间的起点终点
两个单调队列是从1开始维护的,但所求满足条件的最长连续子序列肯定不全从1开始,也就是肯定有滑动窗口,不过这题滑动窗口的长度不是固定的,但起点是单调队列里的元素,由于单调队列的元素最多出队入队一次,因此枚举终点时可以O(1)求出起点。
我们设定一个起点last,max-min大于k的时候,说明这个起点对应满足条件的终点已经跑完了,再往后延伸终点,max-min只可能更大
这时候需要一个队首出队作为下一个起点,冷静分析一波,出队id较前的那个队首:出队较后的,那么较前的也不包括在里面了;并且区间要尽量长所以起点尽量前
单调队列这东西文字还是太苍白了,直接看代码比较好
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<deque> 6 #define LL long long 7 #define debug(x) cout << "[" << x << "]" << endl 8 using namespace std; 9 10 const int mx = 1e5+10; 11 struct node{ 12 int num, id; 13 node(int num = 0, int id = 0): num(num),id(id){} 14 }; 15 deque<node> qmin, qmax; 16 17 int main(){ 18 int n, m, k; 19 while (~scanf("%d%d%d", &n, &m, &k)){ 20 int a, ans = 0, last = 0; 21 qmin.clear(); 22 qmax.clear(); 23 for (int i = 1; i <= n; i++){ 24 scanf("%d", &a); 25 while (!qmin.empty() && qmin.back().num >= a) qmin.pop_back(); 26 qmin.push_back(node(a, i)); 27 while (!qmax.empty() && qmax.back().num <= a) qmax.pop_back(); 28 qmax.push_back(node(a, i)); 29 while (qmax.front().num-qmin.front().num > k){ 30 if (qmin.front().id > qmax.front().id){ 31 last = qmax.front().id; 32 qmax.pop_front(); 33 } 34 else { 35 last = qmin.front().id; 36 qmin.pop_front(); 37 } 38 } 39 if (qmax.front().num-qmin.front().num >= m) ans = max(ans, i-last); 40 } 41 printf("%d ", ans); 42 } 43 return 0; 44 }