为了学习单调队列优化dp去了解了前置知识。
什么是单调队列呢?
顾名思义,单调队列的重点分为 “单调” 和 “队列”
“单调” 指的是元素的的 “规律”——递增(或递减)
“队列” 指的是元素只能从队头和队尾进行操作
实际上维护一段变化区间里的单调的序列。
如oiwiki中有下面的例子
原序列为
1 3 -1 -3 5 3 6 7
要求的是每连续的 k 个数中的最小值,
操作如下
操作 | 队列状态2 |
---|---|
1 入队 | {1} |
3 比 1 大,3 入队 | {1 3} |
-1 比队列中所有元素小,所以清空队列 -1 入队 | {-1} |
-3 比队列中所有元素小,所以清空队列 -3 入队 | {-3} |
5 比 -3 大,直接入队 | {-3 5} |
3 比 5 小,5 出队,3 入队 | {-3 3} |
-3 已经在窗体外,所以 -3 出队;6 比 3 大,6 入队 | {3 6} |
7 比 6 大,7 入队 | {3 6 7} |
总结下来就是:一旦遇到了破环队列单调性的值,就弹出之前队列中的数值,始终保持队列递增的状态.保证队首始终是当前区间里最小的值.
例题
滑动窗口
head tail维护单调队列中的队首和队尾 发现新元素
枚举i来遍历整个数列
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 11;
int n,k;
int a[N];
int qx[N],qn[N];
void getmax(){//求最大值,保持队列单减,保证队首最大
int head = 0,tail = 0;
for(int i = 1; i <= k; i++){
while(head <= tail && a[qx[tail]] <= a[i]) tail--;
//找到前k个数中的最值 一旦遇到了破环队列单调性的值 就清空数列
//遇到大的就不符合
qx[++tail] = i;
}
for(int i = k; i <= n; i++){//同理
while(head <= tail && a[qx[tail]] <= a[i]) tail--;
qx[++tail] = i;
while(qx[head] <= (i - k)){//因为题目中限制区间长度为k,队首位置超出区间也要出队
head ++;
}
cout << a[qx[head]] << " ";
}
}
void getmin(){
int head = 0,tail = 0;
for(int i = 1; i <= k; i++){
while(head <= tail && a[qn[tail]] >= a[i]) tail--;
qn[++tail] = i;
}
for(int i = k; i <= n; i++){
while(head <= tail && a[qx[tail]] >= a[i]) tail--;
qn[++tail] = i;
while(qn[head] <= (i - k)){
head ++;
}
cout << a[qn[head]] << " ";
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
getmin();
cout << endl;
getmax();
cout << endl;
return 0;
}
另一道类似的题
洛谷P2698 [USACO12MAR]Flowerpot S
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 11;
int n,d;
pair<int,int>a[N];
int q1[N];
int q2[N];
int h1,h2,t1,t2;
int main(){
ios::sync_with_stdio(false);
cin >> n >> d;
for(int i = 1; i <= n ; i++){
cin >> a[i].first >> a[i].second ;
}
sort(a + 1, a + 1 + n);
h1 = h2 = 1;
int l = 1,r = 0;
int ans = 0x3f3f3f3f;
for(l = 1; l <= n; l++){
while(h1 <= t1 && q1[h1] < l)h1++;//维护区间 如果队首在左端点左边就右移
while(h2 <= t2 && q2[h2] < l)h2++;
while(a[q1[h1]].second - a[q2[h2]].second < d && r < n){
r++;
while(h1 <= t1 && a[q1[t1]].second < a[r].second)t1--;//注意是队尾和边界比较
q1[++t1] = r;
while(h2 <= t2 && a[q2[t2]].second > a[r].second)t2--;
q2[++t2] = r;
}
if(a[q1[h1]].second - a[q2[h2]].second >= d){
ans = min(ans,a[r].first - a[l].first);
}
}
if(ans < 0x3f3f3f3f)cout << ans << endl;
else puts("-1");
return 0;
}