单调栈

单调栈的定义:

元素顺序始终保持单调性(递增、递减)的栈。
(递增单调栈和递减单调栈的区别仅限于单调顺序不同,故以下为了方便讲解,默认提到的单调栈都为严格递增单调栈。)

维护方法:

压入新元素 \(x\) 时,将 \(x\) 与栈顶元素比较,若 \(x\) 小于等于栈顶元素,则将栈顶元素弹出,再与新的栈顶元素比较,直到 \(x\) 大于栈顶元素。


举例:

有一组数7,8,9,2,10,13,将这6个数从左往右压入严格递增单调栈:
1、压入7,此时栈为空,无栈顶元素,操作完成后栈内元素为:{7};
2、压入8,此时栈顶元素为7,7<8,故可以直接压入,操作完成后栈内元素为:{7、8};
3、压入9,此时栈顶元素为8,8<9,故可以直接压入,操作完成后栈内元素为:{7、8、9};
4、压入2,此时栈顶元素为9,9>2,故需要弹出栈顶元素9;同理,弹出8、7。此时栈为空,可以压入2,操作完成后栈内元素为:{2};
5、压入10,此时栈顶元素为2,2<10,故可以直接压入,操作完成后栈内元素为:{2、10};
6、压入13,此时栈顶元素为10,10<13,故可以直接压入,操作完成后栈内元素为:{2、10、13};
7、清空栈内元素;

使用单调栈时,一般是在弹出元素的过程中进行统计操作(即x小于等于栈顶元素的情况);


例题:

Largest Rectangle in a Histogram

题目大意:给定 \(n\) 个高为 \(a_1、a_2、...、a_n\) 的柱子,求该柱状图中,可勾勒出的最大的矩形面积。

思路:选择某个柱子 \(i\) ,将 \(i\) 向两边拓展,若碰到比 \(a_i\) 高的柱子就接着拓展,若碰到比 \(a_i\) 小的柱子就停止拓展。
直接暴力做的时间复杂度是 \(O(n^2)\) ,但可以使用单调栈优化。
考虑向一个递增单调栈中按 \(i\) 从小到大的顺序压入 \(i\) ,设栈顶元素为 \(top\) ,若 \(a_i < a_top\) ,则在弹出 \(top\) 的同时,将答案 \(ans\)\(top\) 向左右拓展可得的最大面积取 \(Max\),并更新第 \(i\) 根柱子向左端能拓展到的最远处。
\(i\) 根柱子向最左端拓展的最远处可以在压入 \(i\) 之前弹出 \(top\) 的过程中更新,而最右端则应在弹出 \(i\) 的时候被更新。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define int long long
using namespace std;
const int N = 8e4 + 5;
int n, ans, cnt, a[N], stck[N];
signed main () {
	scanf ("%lld", &n);
	for (int i = 1; i <= n; i ++)
		scanf ("%lld", &a[i]);
	for (int i = 1; i <= n; i ++) {
		while (cnt && a[stck[cnt]] <= a[i]) {
			ans += i - stck[cnt] - 1;
			cnt --;
		}
		stck[++ cnt] = i;
	}
	for (int i = 1; i <= cnt; i ++)
		ans += n - stck[i];
	printf ("%lld\n", ans);
	return 0;
}

你可能感兴趣的:(单调栈)