参考来自:https://endlesslethe.com/monotone-queue-and-stack-tutorial.html#i-2
单调栈
一个单调递增栈的例子:
进栈元素分别为3,4,2,6,4,5,2,3
第i步 | 操作 | 结果 |
---|---|---|
1 | 3进栈 | 3 |
2 | 3出栈,4进栈 | 4 |
3 | 2进栈 | 4 2 |
4 | 2、4出栈,6进栈 | 6 |
5 | 4进栈 | 6 4 |
6 | 4出栈,5进栈 | 6 5 |
7 | 2进栈 | 6 5 2 |
8 | 2出栈,3进栈 | 6 5 3 |
对于一个元素i:
- 左边区间第一个比它大(小)的元素;
- 确定这个元素是否为区间最值
- 右边区间第一个大于它的值
- 到右边区间第一个大于它的值的距离
- 确定以该元素为最值得最长区间
代码
//在“尾部”添加元素
while (r != 0 || ms[r] <= x) r--;
ms[++r] = x;
//查询栈顶元素
if (r != 0) printf("%d\n", ms[r]);
else printf("-1");
这里可以把x换成元素x得下标压入栈
单调队列
一个递增单调队列的例子
队列大小不能超过3,入队元素依次为3,2,8,4,5,7,6,4
第i步 | 操作 | 结果 |
---|---|---|
1 | 3入队 | 3 |
2 | 3从队尾出队,2入队 | 2 |
3 | 8入队 | 2 8 |
4 | 8从队尾出队,4入队 | 2 4 |
5 | 5入队 | 2 4 5 |
6 | 2从队头出队,7入队 | 4 5 7 |
7 | 7从队尾出队,6入队 | 4 5 6 |
8 | 6、5、4从队尾出队,4入队 | 4 |
- 可以查询区间最值(不能维护区间第k大)
- 优化DP
代码
//在“尾部”添加元素x
while (l != r && mq[r] <= x) r--;
mq[++r] = x;
//查询队首元素
if (l != r) printf("%d\n", mq[l+1]);
else printf("-1\n");
//弹出队首元素
if (l != r) l++;
下面直接见练习题(持续收集)
1. POJ 2823 Sliding Window
链接:POJ 2823
**题意:**就是给你n 和 k,表示元素的个数, 以及区间长度, 要求输出从1到n-k为起始下标的每个长度为k的区间的最小值输出,以及最大值输出。
**题解:**这里用单调队列写,记录的是下标,并且判断一下当前的最值的下标在不在当前的区间内就可,具体看代码。
代码:
//#include <bits/stdc++.h>
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 1e6+50;
int n, len, l, r;
int a[maxn];
int mq[maxn];
int main()
{
scanf("%d %d",&n, &len);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
///1 3 -1 -3 5 3 6 7
///3 2 8 4 5 7 6 4
l = 0, r = 0;
for(int i = 1; i <= n; i++){
while(l != r && a[mq[r-1]] >= a[i]){
r--;
}
mq[r++] = i;
while(mq[l] <= i - len) l++;
//for(int j = l; j < r; j++)
// cout << a[mq[j]] << " ";
//cout << endl;
if(i >= len) cout << a[mq[l]] << " ";
}
cout << endl;
l = 0, r = 0;
for(int i = 1; i <= n; i++){
while(l != r && a[mq[r-1]] <= a[i]) r--;
mq[r++] = i;
while(mq[l] <= i - len) l++;
if(i >= len) cout << a[mq[l]] << " ";
}
cout << endl;
return 0;
}
2.POJ 3250 Bad Hair Day
链接:POJ 3250
**题意:**给你n头奶牛的高度,从左到右依次排列,都向右看,现在要你求每一头奶牛向右看可以看到的奶牛总数。
**题解:**单调队列简单题
代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 1e6+60;
long long n, a[maxn];
long long h[maxn], w[maxn], cur = 0;
long long ans = 0;
const int INF = 0x3f3f3f3f;
void solve()
{
h[0] = INF+1;
for(int i = 1; i <= n+1; i++)
{
if(a[i] < h[cur])
{
h[++cur] = a[i];
w[cur] = cur-1;
}
else
{
while(a[i] >= h[cur])
{
ans += w[cur];
h[cur--] = 0;
if(cur == 0)
break;
//cur--;
}
h[++cur] = a[i];
w[cur] = cur-1;
}
if(cur == 0)
return;
}
}
int main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
a[n+1] = INF;
solve();
cout << ans << endl;
return 0;
}