单调队列优化DP
单调队列优化DP(解决部分求最值的DP优化)
单调队列
队列中的元素其对应在原来的列表中的顺序必须是单调递增的。
队列中元素的大小必须是单调递(增/减/甚至是自定义也可以)
滑动窗口
有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
我们开两个队列,这两个队列只可以从队首入队,可以从队首和队尾出队。
用 \(a\) 来存数据,用 \(b\) 来存入队顺序。
例如:
8 3
1 3 -1 -3 5 3 6 7
假设我们现在在求最小值,我们来模拟一下过程。
1入队,a={1},b={1}
3入队,a={1,3},b={1,2}
-1入队,显然,前面的1和3无论如何都不会成为以i=3为结尾的区间的min,所以弹出1和3
a={-1},b={3}
-3入队,同上,弹出-1
a={-3},b={4}
5入队,-3有可能可以成为min,保留
a={-3,5},b={4,5}
3入队,同第三条,5不可能成为min了,弹出
a={-3,3},b={4,6}
6入队,3有可能成为min,保留。而-3脱离区间,弹出
a={3,6},b={6,7}
7入队,6有可能成为min,保留
a={3,6,7}, b=
过程如下:
- 如果新数大于等于旧数,保留旧数
- 如果新数小余旧数,弹出旧数
- 如果head脱离范围,弹出
每次询问查询a[head]即可
模板
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,m,s[1000005];
ll a[1000005],b[1000005],head,tail;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>s[i];
}
head=1,tail=0;
for(int i=1;i<=n;i++){//求min
while(1){
if(s[i]<=a[tail]&&tail>=head){//一定是 tail>=head,因为tail可以顶替掉head
tail--;
}
else{
break;
}
}
a[++tail]=s[i];
b[tail]=i;
while(1){
if(b[head]<=i-m){
head++;
}
else{
break;
}
}
if(i>=m){
cout<<a[head]<<" ";
}
}
cout<<endl;
head=1,tail=0;
memset(a,0,sizeof a);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++){//求max
while(1){
if(s[i]>=a[tail]&&tail>=head){
tail--;
}
else{
break;
}
}
a[++tail]=s[i];
b[tail]=i;
while(1){
if(b[head]<=i-m){
head++;
}
else{
break;
}
}
if(i>=m){
cout<<a[head]<<" ";
}
}
}
可以自由转载

浙公网安备 33010602011771号