单调栈 Monotonic Stack

单调栈 Monotonic Stack

类似单调队列,区别是只在一端进行操作。

模板:最近更小值(Nearest Smaller Element)问题

CSES Nearest Smaller Values: 长度为N (0N1050 \leq N \leq 10^5)的整数数组aa,对于每个位置ii,请找到最大的jj,使得j<ij < iai>aja_i > a_j

这题的解法就是从左向右扫描数组,同时维护一个单调上升的栈,当新的元素大于栈顶元素时,弹出栈顶元素直到小于当前元素。这样栈顶元素就是我们要的结果。

#include <bits/stdc++.h>
 
using namespace std;
 
int N;
 
int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
 
	cin >> N;
	stack<pair<int, int>> stack;
	stack.push({0, 0});
 
	for(int i = 1; i <= N; ++i) {
		int a; cin >> a;
		while(!stack.empty() && stack.top().first >= a) stack.pop();
		cout << stack.top().second << " ";
		stack.push({a, i});
	}
}

综合例题

例题:POJ 3250 Bad Hair Day,给定nn头牛的身高,每头牛求它到它右边第一个比它高(或身高相等)的牛之间有多少头牛,然后将求得的结果相加就是最后的答案。

用单调栈的解法,是维护一个身高依次下降的牛的位置的栈。如果向右依次扫描时,碰到身高更低的牛就入栈。反之,则要把所有不符合顺序栈顶的牛出栈(同时也就找到了这头牛右侧第一个比它高的牛),再将当前的牛入栈。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<stack>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long LL;
 
int main()
{
	int i,n,top,a[80010]; //top指向栈顶元素 
	LL ans; //存储最终结果 
	stack<int> st; //st为栈,每次入栈的是每头牛的位置 
	while(~scanf("%d",&n))
	{
		while(!st.empty()) st.pop(); //清空栈 
		for(i=0;i<n;i++)
			scanf("%d",&a[i]);
		a[n]=inf; //将最后一个元素设为最大值 
		ans=0;
		for(i=0;i<=n;i++) {
			if(st.empty()||a[i]<a[st.top()]) { //如果栈为空或入栈元素小于栈顶元素,则入栈 
				st.push(i);
			} else {
				while(!st.empty()&&a[i]>=a[st.top()]) { //如果栈不为空并且栈顶元素不大于入栈元素,则将栈顶元素出栈 
					top=st.top(); //获取栈顶元素 
					st.pop();     //栈顶元素出栈 
					//这时候也就找到了第一个比栈顶元素大的元素 
					//计算这之间牛的个数,为下标之差-1 
					ans+=(i-top-1);	
				}
				st.push(i); //当所有破坏栈的单调性的元素都出栈后,将当前元素入栈 
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

习题

习题
CSES1142CSESAdvertisement普及+/提高解答
P3668USACO GoldModern Art 22017提高+/省选-解答