单调队列 & 单调栈学习笔记
Silence_in_Sea · · 算法·理论
单调队列
我们来讲单调队列。
单调队列顾名思义,就是单调递增/递减的队列。
单调队列主要解决的问题是:对于一个序列,用
我们来讲如何求值。
刚刚说了,单调队列顾名思义,就是单调递增/递减的队列。
于是,每放进一个数,我们就拿其从后往前比较。为了更好讲,我们以求
步骤如下:
-
将超出范围的数删除。
-
放入一个数。
-
将这个数与队列末尾的数比较,比到比其小的停止。
-
将队列在这个后面的数均删除。
-
输出队列的头。
样例如下:
8 3
1 3 -1 -3 5 3 6 7
单调队列变化过程见 OI-wiki。
具体应用有一定变化,我们来讲解一下。
1.单调队列优化 dp
个人不太喜欢,但还是讲一下。
以 P1725 为例题。
dp 比较好想,
删除也要以
放个代码:
#include<bits/stdc++.h>
using namespace std;
const long long INF=-4e18;
long long a[200005],dp[200005];
int q[200005],h=1,t=1;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,l,r;cin>>n>>l>>r;
for(int i=0;i<=n;i++)cin>>a[i];
dp[0]=0;q[1]=0;
for(int i=1;i<=n;i++){
while(h<=t&&q[h]<i-r)++h;
if(i>=l&&h<=t)dp[i]=dp[q[h]]+a[i];
else dp[i]=INF;
int k=i-l+1;
if(k>=0){
while(h<=t&&dp[k]>=dp[q[t]])--t;
q[++t]=k;
}
}
long long ans=INF;
for(int i=max(0,n-r+1);i<=n;i++)ans=max(ans,dp[i]);
cout<<ans<<"\n";
return 0;
}
2.单调队列求定长区间和最大或最小
乍一看这个标题可能比较奇怪,那么我们看这道例题:P1419,可能会清楚一点。
首先那个二分答案我们不说了,我们直接来说 check 的单调队列。
首先我们将每个数减去
然后,我们建立前缀和
我们现在要求长度在
转化。
我们来到第
我们只要找出
找出后与
然后没什么了,自己随便找点题练习吧,如果发现有别的类型可以联系我补。
单调栈
个人感觉单调栈用的比较少。
首先,我们要理解,单调栈和单调队列的区别其实就是栈和队列的区别。
看 P5788。
我们思考,对于
了解这一点,单调栈就很容易理解了。
步骤如下:
-
弹出栈头小于等于
a_i 的。 -
输出栈头。
-
放入
i 。
我们来讲点应用。
1.最大面积问题
以 P4147 为例。(其实 P1950 也是可以尝试一下的,但作者不想写就没做。)
这题不完全算是单调栈我觉得,但是也有一定的单调栈的思想去 dp。
我认为 这篇题解 还是很好的,如果一眼没秒掉建议阅读这篇题解。
具体思路其已经讲的很清楚了,不多赘述,不会的可以问我,能力范围内愿意回答。
2.不知道
我也不知道以什么为标题比较好,但扶苏的 B3666 真的是很好的一道单调栈模板,值得一做。
这其实难度不大,但真正诠释了单调栈的定义。首先,单调栈中的数一定递增,且在第
然后没什么了,自己随便找点题练习吧,如果发现有别的类型可以联系我补。