单调栈
常见模型:给定一个序列,求一下每一个数的左边离他最近的且比它小的数在什么地方。
存在逆序的关系,答案一定是右边的那个
#include <iostream>
using namespace std;
int n;
int stk[100000 + 10];
int tt;
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
int x;
cin >> x;
while(tt && stk[tt] >= x) tt--;
if(tt){
cout << stk[tt] << " ";
}
else{
cout << -1 << " ";
}
stk[++tt] = x;
}
return 0;
}
单调队列
解决滑动窗口问题
可以解决区间内,每个长度为k的极值
因为每个区间的重合度实际上是非常高的,如果暴力的话会非常浪费时间
就对每个区间维护一个单调队列
这样维护一个队列,使得每个队列都是维护的它的单调性
那么只需要每一次更换区间的时候,移动到下一个区间进行维护,这时候需要判断队头元素是不是上一个区间的数,如果是需要剔除
因为每个区间都是维护一个单调递增的序列(这里举个单调递增的例子),那么每个区间的最小值就显然是这个单调队列的队头元素
所以是维护单调递增队列是取得最小值,维护单调递减队列是取得最大值
每个数最多被加一次删一次,n个元素就是n次,所以复杂度是O(N)O(N)O(N)
struct queue_in{ // queue_increase
int head;
int tail;
int q[N];
queue_in(){
head = 1;
tail = 0;
memeset(q, 0, sizeof(q));
}
void init(){
head = 1;
tail = 0;
memeset(q, 0, sizeof(q));
}
void push(int x){
while(head <= tail && q[tail] > x) // 先弹出不合法的元素,要注意队列不为空才能进行
tail--;
q[tail++] = x;
}
int front(){
return q[head];
}
void pop(){
head ++;
}
int size(){
return tail - head + 1;
}
}
滑动窗口
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1000000 + 10;
int n, k;
int a[N];
int q[N];
int main(){
cin >> n >> k;
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int hh = 1;
int tt = 0;
for(int i = 1; i <= n; i++){
if(hh <= tt && i - k + 1 > q[hh]) hh++;
while(hh <= tt && a[q[tt]] >= a[i]) tt--;
q[++tt] = i;
if(i >= k) cout << a[q[hh]] << " ";
}
cout << endl;
hh = 1;
tt = 0;
memset(q, 0, sizeof(q));
for(int i = 1; i <= n; i++){
if(hh <= tt && i - k + 1 > q[hh]) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if(i >= k) cout << a[q[hh]] << " ";
}
cout << endl;
return 0;
}
由此我们可以引申出来很多结论,这时候这个区间是有单调性的,由于单调性的特性,我们可以考虑在实际问题中,怎么运用单调性来进行二分等等优化操作
1006

被折叠的 条评论
为什么被折叠?



