单调栈与单调队列

一,单调栈

思想:

可以用于维护一个数前/后第一个大于/小于他的数。我们以维护最大值为例

  1. 对于数x,求取它右边第一个大于他的数y。显然,[x,y]两个数中间的数都是比x小的。也就是,我们在求出x右边第一个大于它的数之前,一定能够先找到中间这些数的右边第一个比他大。(不可能超过y,因为中间数mid<=x<=y).
  2. 利用这个思想,我们把还未求出右边第一大的数放入栈里面,如果找到栈顶的右边第一大,弹出栈顶。否则,栈顶更新为这个更小的数(它一定不会比原栈顶慢找到右边第一大)
  3. 那么如何求取左边第一大呢?显然,我们在比较栈顶时就已经实现比较了,求目前数x的左边第一大。
    1. 如果栈顶k比x大,显然k就是x的左边第一大(栈里面的元素显然都比k大,[k,x]之间不存在比k大的数,否则k早就被弹出了。
    2. 如果k小于x,等于k找到右边第一大,弹出k,继续比较x与新栈顶,直到栈空或者找到比k大。
    3. 但是你会发现,k等于x怎么办?我们可以在入栈时,记录每个元素栈里面比它严格大的值的下标,如果k=x,x存储k所记录的比他大的下标即可(可能没有)

P5788 【模板】单调栈 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
#define endll            endl<<endl
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<long long, long long> pll;
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
//double 型memset最大127,最小128
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 3e6 + 10;
int a[N];
void mysolve()
{
	stack<pii>s;
	int n,x;
	cin>>n;
	for(int i=1; i<=n; ++i)
		{
			cin>>x;
			while(!s.empty()&&s.top().first<x)
				{
					a[s.top().second]=i;
					s.pop();
				}
			s.push({x,i});
		}
	for(int i=1; i<=n; ++i)cout<<a[i]<<" \n"[i==n];
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

 二,单调队列

单调队列的元素在按照进入顺序排序的同时维护了区间单调性。

P1886 滑动窗口 /【模板】单调队列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  1. 我们用单调队列维护最大值,即队列是递减的。队列存储的元素至多是包含当前位置的前k个数
  2. 设当前遍历到i,值为x。如果x比队尾大,就删除队尾,直到队列空或者队尾比x大。(因为x比队列里的元素晚出现,所以队列里比他小的数,后面都没有他们什么事了)
  3. 如果x比队尾小,放入队尾。
  4. 注意,每次插入,先判断队首是否下标符合[i-k+1,i]的区间,否则舍去。
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
typedef pair<int, int> pii;
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
//double 型memset最大127,最小128
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 3e5 + 10;

void mysolve()
{
	int n,k;
	cin>>n>>k;
	deque<pii>q1,q2;
	vector<int>a1,a2;
	int x;
	for(int i=1; i<=n; ++i)
		{
			cin>>x;
			if(i<k)
				{
					while(!q1.empty()&&q1.back().first<x)q1.pop_back();
					q1.push_back({x,i});

					while(!q2.empty()&&q2.back().first>x)q2.pop_back();
					q2.push_back({x,i});
				}
			else
				{
					while(!q1.empty()&&q1.front().second<=i-k)q1.pop_front();
					while(!q1.empty()&&q1.back().first<x)q1.pop_back();
					q1.push_back({x,i});
					a1.push_back(q1.front().first);

					while(!q2.empty()&&q2.front().second<=i-k)q2.pop_front();
					while(!q2.empty()&&q2.back().first>x)q2.pop_back();
					q2.push_back({x,i});
					a2.push_back(q2.front().first);
				}
		}
	for(int i=0; i<=n-k; ++i)
		{
			if(!i)cout<<a2[i];
			else cout<<" "<<a2[i];
		}
	cout<<endl;
	for(int i=0; i<=n-k; ++i)
		{
			if(!i)cout<<a1[i];
			else cout<<" "<<a1[i];
		}
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

例题:P2698 [USACO12MAR]Flowerpot S 

  1. 我们求区间[L,R]是否是合法区间,否则,显然是需要把区间继续扩大。
  2. 所以,我们可以先固定L,不断往右移动R,显然第一个符合条件的R是最优的,那么,我们这时才把L往后移动。
  3. 这样遍历只需O(n)。
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
typedef pair<int, int> pii;
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
//double 型memset最大127,最小128
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const int N = 3e5 + 10;
pii a[N];
void mysolve()
{
	int n,d;
	cin>>n>>d;
	int ans=INF;
	for(int i=1; i<=n; ++i)cin>>a[i].first>>a[i].second;
	deque<pii>q1,q2;
	sort(a+1,a+1+n);
	int p=-1;
	for(int i=1; i<=n; ++i)
		{
			while(!q1.empty()&&q1.back().first<a[i].second)q1.pop_back();
			q1.push_back({a[i].second,a[i].first});

			while(!q2.empty()&&q2.back().first>a[i].second)q2.pop_back();
			q2.push_back({a[i].second,a[i].first});

			if(q1.front().first-q2.front().first>=d)
				{
					ans=min(ans,abs(q1.front().second-q2.front().second));//队列的队首就是整个队列下标最小的,所以最大值与最小值的L就是min那一个
					p=min(q1.front().second,q2.front().second);
					while(!q1.empty()&&q1.front().second<=p)q1.pop_front();//合法后L右移,即删除小标小于等于L的元素
					while(!q2.empty()&&q2.front().second<=p)q2.pop_front();
				}
		}
	cout<<(ans<INF?ans:-1)<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值