关于单调栈...

单调栈:单调递增或单调减的栈,主要通过push和pop两个操作巧妙的解决一些有关序列的问题。


首先肯定是模板题:链接: link.
题意:给出整数数列a[1]到a[n],求f[1]~f[n],f[i]表示第i位置后面第一个大于a[i]的数的下标。
思路:从左到右遍历,碰到一个数小于等于栈顶就入栈,否则依次出栈,同时出栈的下标的f[i]就是当前遍历到的这个下标。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<stack>
#define ll long long
using namespace std;
const int mod = 998244353;
const int maxn =3e6+10;
ll a[maxn];
ll f[maxn];
stack<ll> s;
int main(){
	int n;
	cin >> n;
	for(int i = 1; i <= n; i++){
		scanf("%lld", a+i);
	}
	for(int i = 1; i <= n; i++){
		if(s.empty() || a[i]<=a[s.top()]){	//当前下标的数小于等于栈顶下标的数
			s.push(i);
		}else{
			while(s.size() && a[i]>a[s.top()]){	//当前下标的数大于栈顶下标的数
				int top = s.top();	
				s.pop();
				f[top] = i;	//当前的下标一定是第一个大于top的下标
				// cout << top << " " << i << endl;
			}
			s.push(i);
		}
	}
	for(int i = 1; i <= n; i++){
		if(i>1) cout << " ";
		printf("%lld", a[i]);
	}

	return 0;
}


碰到的题目
牛客链接: link.
题意:有n个小于k的数,不改变数组的顺序,求字典序最小的1到k的子序列。

#include <bits/stdc++.h>
#define ll long long
#define T int T;scanf("%d", &T);while(T--)
using namespace std;
const int mod = 998244353;
const int maxn =1e6+10;
int a[maxn];
int last[maxn];
int vis[maxn];
vector<int> ans;
int main(){
	int n,k;
	cin >> n >> k;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		last[a[i]] = i;		//记录每个数最后一个位置的下标
	}
	for(int i = 1; i <= n; i++){
		if(vis[a[i]]) continue;	//记录当前数是不是已选,选了就continue
		while(ans.size() && last[ans[ans.size()-1]]>i && ans[ans.size()-1]>a[i]){
			//如果当前数小于栈顶并且后面还有栈顶这个数,那么先出栈,后面再入(为了字典序最小)
			vis[ans[ans.size()-1]] = 0;
			ans.pop_back();
		}
		vis[a[i]] = 1;	//选当前数
		ans.push_back(a[i]);
	}

	for(int i = 0; i < ans.size(); i++){
		if(i)  cout << " ";
		cout << ans[i];		
	}

	return 0;
}


再来几道经典例题

例1 链接: link.

题意 有n头牛,每头牛可以看到右侧比他矮的牛,求每头牛可看到牛的总数。
思路 利用单调栈,碰到一头牛判断与栈顶的牛的高度,低于栈顶的牛就把当前牛入栈(下标),否则,边出栈边计算下标差值(可看到牛的数量)。
注意 最后加一头1e9的牛便于栈清空和栈牛剩余牛的可见数量的计算。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<stack>
#define ll long long
using namespace std;
const int mod = 998244353;
const int maxn =1e6+10;
ll a[maxn];
stack<ll> s;
int main(){
	int n;
	ll ans;
	while(~scanf("%d", &n)){
		ans = 0;
		for(int i = 0; i < n; i++){
			scanf("%lld", a+i);
		}
		a[n] = 1e9;	//为了最后清空栈
		for(ll i = 0; i <= n; i++){
			if(s.size()==0 || a[i] < a[s.top()]){	
				s.push(i);
			}else{
				ll top;
				while(s.size() && a[i] >= a[s.top()]){
					top = s.top();	//出栈第top只牛
					s.pop();
					ans += (i-top-1); 	//计算top和i之间的牛的数量等于top这个牛可看到的数量
				}
				s.push(i);		//把当前牛入栈(下标) 为了计算
			}
		}
		printf("%lld\n", ans);
	}

	return 0;
}

例2 链接: link.

题意:已知n个矩形的高度,宽度都为1,求最大面积。
在这里插入图片描述
思路:利用单调栈,每次遇到一个矩形,判断与栈顶的关系,若当前高度>栈顶矩形,则入栈,否则,依次把栈里的矩形出栈,并计算以出栈的矩形为最高高度的大矩形的面积(当前的矩形不进入计算)。
注意:在最后加一块高度为-1的矩形便于最后栈的清空与栈内矩形计算。
代码

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<stack>
#define ll long long
using namespace std;
const int mod = 998244353;
const int maxn =1e6+10;
ll a[maxn];
stack<ll> s;
int main(){
	int n;
	ll ans;
	while(~scanf("%d", &n)){
		if(n==0) break;
		ans = 0;
		for(int i = 0; i < n; i++){
			scanf("%lld", a+i);
		}
		a[n] = -1;	//为了最后清空栈
		ans = 0;
		ll cnt = 0;
		for(ll i = 0; i <= n; i++){
			if(s.size()==0 || a[i]>=a[s.top()]){ //空栈或当前高度大于等于栈顶
				s.push(i);
			}else{
				ll top;
				while(s.size() && a[i] < a[s.top()]){
					top = s.top();	//向左延伸的矩形下标
					s.pop();
					cnt = (i-top)*a[top]; //以出栈的矩形作为最高高度,i-top表示延伸到左侧的矩形数量
					ans = max(ans,cnt);
				}
				s.push(top);	//可延伸到的最左侧入栈
				a[top] = a[i]; 	//入栈的高度为当前的高度
			}
		}
		printf("%lld\n", ans);
	}

	return 0;
}

单调栈是一种常用的数据结构,用于解决一些与单调性相关的问题。它可以在 O(n) 的时间复杂度内解决这些问题。 单调栈的基本思想是维护一个单调递增或单调递减的栈。在处理每个元素时,我们将栈中比当前元素小(或大)的元素弹出栈,然后将当前元素入栈。这样可以确保栈中的元素始终保持单调性。 关于单调栈的应用场景,其中一个常见的问题是找到数组中每个元素的下一个更大元素(Next Greater Element)。给定一个数组,对于每个元素,需要找到它右边第一个比它大的元素。使用单调栈可以在线性时间内解决这个问题。 下面是一个使用单调栈解决 Next Greater Element 问题的示例代码: ```cpp vector<int> nextGreaterElement(vector<int>& nums) { int n = nums.size(); vector<int> result(n, -1); // 初始化结果数组为-1 stack<int> st; // 单调递减栈,存储数组索引 for (int i = 0; i < n; i++) { while (!st.empty() && nums[st.top()] < nums[i]) { result[st.top()] = nums[i]; // 当前元素是栈顶元素右边第一个更大的元素 st.pop(); } st.push(i); } return result; } ``` 以上代码中,我们使用一个栈来存储数组的索引。遍历数组,对于每个元素,如果栈不为空且栈顶元素小于当前元素,说明栈顶元素的下一个更大元素就是当前元素。将栈顶元素出栈,并更新结果数组中对应位置的值为当前元素。最后将当前元素的索引入栈。遍历完成后,返回结果数组即可。 除了 Next Greater Element 问题,单调栈还可以用于解决其他一些与单调性相关的问题,如 Next Smaller Element、Largest Rectangle in Histogram 等。希望这个模板能对你有所帮助!有其他问题欢迎继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值