浅谈单调队列及单调队列与单调栈的区别

单调队列:具有单调性的队列,分为两种:单调递增队列和单调递减队列。和单调栈相似都是将复杂度优化到O(n)
单调队列与单调栈的区别:
单调栈只维护一端(栈顶)单调队列维护两端,它的头端可以出数,尾部可以进数。
单调栈通常维护全局的单调性,而单调队列通常维护局部的单调性。
单调栈大小没有上限,而单调队列通常有大小限制。
由于单调队列的对首可以出队以及前面的元素一定比后面的元素先入队的性质,使得它可以维护局部的单调性。因此单调队列通常用于解决局部性的最值问题。
接下来看两个例题:
ZJM 有一个长度为 n 的数列和一个大小为 k 的窗口, 窗口可以在数列上来回移动. 现在 ZJM 想知道在窗口从左往右滑的时候,每次窗口内数的最大值和最小值分别是多少.
input
输入有两行。第一行两个整数n和k分别表示数列的长度和滑动窗口的大小,1<=k<=n<=1000000。第二行有n个整数表示ZJM的数列。
output
输出有两行。第一行输出滑动窗口在从左到右的每个位置时,滑动窗口中的最小值。第二行是最大值。
Sample Input
8 3
1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3
3 3 5 5 6 7

分析题目知道正是求解局部性的最值问题,可以使用单调队列。

#include<iostream>
#include<cstdio>
#define inf 1000010
using namespace std;
int n,k;
int a[inf],q[inf];
int minn[inf],maxn[inf];
void solve1(){
	int l=1,r=0;
	for(int i=1;i<=n;i++){
		while(l<=r&&a[q[r]]>a[i]){//维护单调性
			r--;	
		}
		q[++r]=i;
	//	cout<<l<<' '<<r<<endl;
		if(q[r]-q[l]+1>k)l++;//维护局部性,区间大小<=k
		minn[i]=a[q[l]]; 
	}
	for(int i=k;i<=n;i++)cout<<minn[i]<<" ";cout<<endl;
}
void solve2(){
	int l=1,r=0;
	for(int i=1;i<=n;i++){
		while(l<=r&&a[q[r]]<a[i]){
			r--;
		}
		q[++r]=i;
		if(q[r]-q[l]+1>k)l++;
		maxn[i]=a[q[l]];
	}
	for(int i=k;i<=n;i++)cout<<maxn[i]<<' ';cout<<endl;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	solve1();
	solve2();
	return 0;
} 

例题二:
一个长度为n的整数序列,从中找出一段不超过m的连续子序列,使得整个序列的和最大。
例如 1,-3,5,1,-2,3
当m=4时,S=5+1-2+3=7
当m=2或m=3时,S=5+1=6
Input
第一行是两个正数n, m ( n, m ≤ 300000 )
第二行是n个整数
Output
每个测试用例输出一行:一个正整数,表示这n个数的最大子序和长度
Sample Input
6 4
1 -3 5 1 -2 3
Sample Output
7
题解:
我们可以先求出数列的前缀和存到数组sum[ ]中,那么任意连续的子序列和就是sum[ i ]-sum[ j ] (i>j && j>i-m)。那么问题就转化成了每次找出在(i-m,i)中的最小值min(局部内的最值问题),用sum[i]-min就是对于每个a[i]的最大值。
*另外这道题中如果n个整数都是正整数的话,就可以不用单调队列了,可以使用尺取法。也是O(n)的时间复杂度 *

#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
int a[300010],sum[300010],q[300010];
int main(){
 cin>>n>>m; 
 for(int i=1;i<=n;i++){
 cin>>a[i]; 
 sum[i]=sum[i-1]+a[i];
 }
 int l=1,r=0;
 int ans=0;
 for(int i=1;i<=n;i++){
  while(l<=r&&sum[q[r]]>sum[i]) r--;
  q[++r]=i;
  if(q[r]-q[l]+1>m+1)l++;//要求连续M的子序列,sum[i]-sum[i-m-1],下标差为m+1
  ans=max(ans,sum[i]-sum[q[l]]);
 }
 cout<<ans<<endl;
 return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值