1.单调队列
说明
单调队列,顾名思义,就是一个元素单调的队列,那么就能保证队首的元素是最小(最大)的,从而满足动态规划的最优性问题的需求。
单调队列,又名双端队列。双端队列,就是说它不同于一般的队列只能在队首删除、队尾插入,它能够在队首、队尾同时进行删除。
应用
1. 刚刚接触单调队列,就只做过一道经典例题,(洛谷滑动窗口),这道题还是很理解的,就是求区间长度为K的最大值,最小值,用单调队列存长度为K最值的下标。
对着代码看更容易理解:我是这样理解的 ps:这里我是用数组模拟的单调队列
#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int MAXN = 1000000 + 10;
int a[MAXN],l=1,r=0,k,n,mians,maans,q[2*MAXN]; //q数组存a数组元素下标
int main() {
IOS
cin >> n >> k;
for(int i=1; i<=n; i++)
cin >> a[i];
//求最小
for(int i=1; i<=n; i++) {
while(q[l] < i-k+1 && l <= r) ++l; //保证队列长度不大于k
while(a[i] <= a[q[r]] && l <= r) --r; //如果有更小的就更新队列
q[++r] = i; //下表放入队列中
if(i >= k) //这个判断是对于一开始还没有访问k个元素的时候
cout << a[q[l]] << " "; //输出队列中最小值
}
cout << endl;
l = 1, r = 0;
//求最大 同上就不解释了
memset(q,0,sizeof(q));
for(int i=1; i<=n; i++) {
while(q[l] < i-k+1 && l <= r) ++l;
while(a[i] >= a[q[r]] && l <= r) --r;
q[++r] = i;
if(i >= k)
cout << a[q[l]] << " ";
}
return 0;
}
2. 听说可以用单调队列优化动态规划等问题,有机会后期补上来。嘻嘻
2.单调栈
说明
单调栈 则是在栈的 先进后出 基础之上额外添加一个特性:从栈顶到栈底的元素是严格递增(or递减) 具体进栈过程如下: 对于单调递增栈,若当前进栈元素为 e,从栈顶开始遍历元素,把小于 e 或者等于 e 的元素弹出栈,直接遇到一个大于 e
的元素或者栈为空为止,然后再把 e 压入栈中。 对于单调递减栈,则每次弹出的是大于 e 或者等于 e 的元素。
应用
单调栈的题我在牛客上做的(区区区间间间)
对于这道题我就不在解释了,因为什么呢?
我认为这位大佬对这道题才是真的理解透彻,讲的明明白白。
大佬地址
还是上一上代码,显得文章不空洞
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define LL long long
LL ll[maxn],rr[maxn],a[maxn];
LL work(LL n){
memset(ll,0,sizeof(ll));
memset(rr,0,sizeof(rr));
stack<int>s,t; //这里也可以用数组代替
//找左区间
for(LL j=1;j<=n;j++){
while(s.size()&&a[j]>=a[s.top()]){ //这里带等于号,和下面不带,目的就是防止遇到相同元素计算重复
s.pop();
}
if(!s.size()) ll[j]=1;
else ll[j]=s.top()+1;
s.push(j);
}
//找右区间
for(LL j=n;j>=1;j--){
while(t.size()&&a[j]>a[t.top()]){
t.pop();
}
if(!t.size()) rr[j]=n;
else rr[j]=t.top()-1;
t.push(j);
}
LL ans=0;
for(LL j=1;j<=n;j++){
ans+=a[j]*(rr[j]-ll[j]+(rr[j]-j)*(j-ll[j]));//对于这个式子如果想不明白就自己找一组数据带入模拟一下,就可以发现其中的奥妙了
}
return ans;
}
int main(){
LL t;
cin>>t;
while(t--){
LL n;
cin>>n;
for(LL j=1;j<=n;j++){
scanf("%d",&a[j]);
}
LL ans=work(n);
for(LL j=1;j<=n;j++){ // 赋值的时候相当于找最大 得到的结果就是负数 正好减去
a[j]=-a[j];
}
ans+=work(n);
cout<<ans<<endl;
}
}