关于单调栈...

本文介绍了单调栈这一数据结构在解决数组和序列相关问题中的应用,包括求解序列中每个位置后面第一个大于当前元素的下标、找到字典序最小的子序列以及计算每头牛能看到的牛的总数和最大矩形面积等经典问题。通过实例解析了单调栈的工作原理和解题思路,展示了其在算法模板和复杂问题简化上的价值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单调栈:单调递增或单调减的栈,主要通过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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值