单调栈与单调队列入门

本文介绍了单调栈和单调队列两种数据结构在解决区间最值问题上的应用,分别通过POJ2796和POJ2823两个实例进行解析。单调栈利用后进先出的特点,保持栈内元素递增或递减,求解区间内所有数乘以最小值的和的最大区间。单调队列则利用双向控制(头尾指针)实现单调性,快速找到滚动屏显示区间的最大值和最小值。这两种数据结构在算法设计中具有重要的作用。
摘要由CSDN通过智能技术生成

单调栈stack:

特点1:top指向最后一个数所在位置
特点2:后进先出
特点3:递增或递减 ****

例题POJ2796

大意:求一个区间,这个区间内 所有数的和sum*区间内min 的值 相比其他区间大
输入:
6
3 1 6 4 5 2
输出:
60
3 5

问题:

  1. 求出每个数在哪个区间内最小: 区间怎么求 ?
  2. 将这个数 * 区间总值:总值怎么算?

思路:

  1. 标记每个数的左右边界是本身 3 [1,1] 1 [ 2, 2] 6 [ 3, 3 ] … 之后,依次入栈。
    入栈前将栈顶元素与之比较 若栈顶元素较大
    { 更改栈顶右区间=入栈元素位置-1)
    更改即将入栈元素左区间=栈顶元素的左区间
    栈顶元素出栈
    即将入栈元素入栈 }
    目的:在其入栈时更改左区间,保证栈内递增,栈内元素的左区间确定;在其出栈时更改右区间,保证了左右区间最大化。
  2. 用 pre[ ]数组储存各个 1-i 的区间值和
    之后令 a[ i ]*(pre[右]- pre[左])
代码:

#include<iostream>
#include<algorithm>

#define ll long long                 
using namespace std;
const int N=1e7+10;

ll n,pre[N];
ll a[N],l[N],r[N];
ll s[N],top=0;
int t=1;

int main()
{
	//scanf("%d",&t);
	while(t--)
	{
		scanf("%lld",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);           //a[i]数组储存各个数
			pre[i] = pre[i-1]+a[i];        //储存【1-i]的区间和值
			l[i] = r[i] = i;               //初始化:a[i]数组中各个数的左右边界为所在位置
		}
		a[n+1] = -1;
		for(int i=1;i<=n+1;i++)
		{
			while(top && a[s[top]] > a[i])   //即将进入的数<栈顶值
			{								
				r[s[top]] = i-1;  			//栈顶值的右区间更改为 i-1
				l[i] = l[s[top]];			//即将进入的数 的左区间 继承 栈顶值的
				top--;						//将栈顶值出栈
			}
			s[++top] = i;	               //入栈
		}
		ll ans=-1,ansl,ansr;
		for(int i=1;i<=n;i++) 
		{
			ll temp = a[i]* (pre[r[i]]-pre[l[i]-1]) ;   //让所有值依次 * 左右区间相减得出的sum
			if(ans<temp) 								//比较出最大
			{
				ans = temp;
				ansl = l[i];
				ansr = r[i];
			}
		}
		printf("%lld\n%lld %lld",ans,ansl,ansr);
	}
	return 0;
} 

双向单调队列deque :

特点1: head 指向最前一个数的前一个位置
特点2: tail 指向最后一个数所在的位置
特点3: 可递增或递减

例题POJ-2823

大意:求滚动屏(显示3个数)区间的最大值,最小值
输入:
8 3
1 3 -1 -3 5 3 6 7
输出:
-1 -3 -3 -3 3 3
3 3 5 5 6 7

Window positionMinimumMaximum
[1 3 -1] -3 5 3 6 7-33
1 [3 -1 -3] 5 3 6 7-33
1 3 [-1 -3 5] 3 6 7-35
1 3 -1 [-3 5 3] 6 7-35
1 3 -1 [-3 5 3] 6 736
1 3 -1 -3 5 [3 6 7]37
代码:
#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e6+20;
const int M=10;
const int inf=0x3f3f3f3f;   //无穷大 

int n,k;
int a[N];
int ansmin[N],ansmax[N];
int que[N],head,tail,t=1;

int main()
{
	while(t--)
	{
		scanf("%d %d",&n,&k);
		for(int i=1;i<=n;i++)	scanf("%d",&a[i]);				//a[i]储存数
		for(int i=1;i<=n;i++)
		{														//求min序列
			while(tail>head && que[head+1]+k<=i) head++;		//保证头+k<=尾
            while(tail>head && a[que[tail]]>a[i]) tail--;		//保证队列单调递增,头就是所求
			que[++tail] = i;									//que[]中存储的是位置
			if(i>=k) ansmin[i-k+1]=a[que[head+1]];				//k及k之后的位置的有答案
		}
		head = tail =0;											
		for(int i=1;i<=n;i++)									//求max序列,同上
		{
			while(tail>head && que[head+1]+k<=i) head++;		//保证头+k<=尾
			while(tail>head && a[que[tail]]<a[i]) tail--;		//保证队列单调递减,头就是所求
			que[++tail] = i;									//que[]中存储的是位置
			if(i>=k) ansmax[i-k+1]=a[que[head+1]];				//k及k之后的位置的有答案
		}
		for(int i=1;i<=n-k+1;i++) printf("%d%c",ansmin[i],i == n-k+1 ?  '\n':' ') ;
		for(int i=1;i<=n-k+1;i++) printf("%d%c",ansmax[i],i == n-k+1 ?  '\n':' ') ;
	}
	return 0;
} 

综上两题:单调栈与单调列

相同点:都将数依次入栈或入列,其间要保证其递增或递减,若不满足的数,则令其出栈或出队,并且更新部分所求,最终得出所求

不同点

  • 单调栈使用top实现后进先出

  • 单调列有head可实现先进先出,tail可实现后进先出,两头控制

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值