单调队列 单调栈

1.单调队列(解决滑动窗口问题)

求长度为k的滑动窗口内的最大/小值

单调队列中的数具有单调性

例如求最大值时,队列左端的数最大,右端的数最小

1.当第i个数进入滑动窗口时,首先判断队列左端的数是否还在窗口内(拿窗口左端下标和队列中的下标对比)

2.判断新来的数是否能替换单调队列中的数,从队列右端开始比较,比如求最大值时,如果新来的数比队列右端的数大,则弹出队列右端,直到队列空或不能替换,将新数插入队列

下面是求滑动窗口最小值的代码,最大值的话将第二步的大小符号调换即可

int hh=0,tt=-1;
//此时第i个数进入滑动窗口 
//窗口的头为i-k+1 
for(int i=0;i<n;i++)
{
	//队列里最大/小值已经不在窗口内了
	//即窗口的头坐标>队列中最值的坐标
	//队列中最值更替,hh++ 
 	if(hh<=tt&&i-k+1>q[hh]) hh++; 
 	//把新进窗口的数和队列里的数对比
	//从队尾开始,可以更替就删除队尾tt-- 
 	while(hh<=tt&&a[q[tt]]>=a[i]) tt--; 
 	//把新的数放到队列合适的位置
	q[++tt]=i;
 	if(i-k+1>=0) cout<<a[q[hh]]<<" ";  
} 


2.单调栈

求一个数左边第一个比他小/大的数

和单调队列相同,单调栈中的元素也具有单调性

例如求左边离他最近的较小值时,单调栈从栈低到栈顶递增

每处理一个新数时,将其与栈顶对比并且弹出,直到栈顶比新数小,将新数压入栈中

由于之前弹出的数一定比栈顶的数要大,所以不会影响之后读入的数的离他最近的最小值

#include<bits/stdc++.h>
using namespace std;//单调栈 
//用于找出序列中每个数左边离它最近的比它大/小的数
int stk[100005],tt; 
int main()
{
	int n;cin>>n;
	for(int i=0;i<n;i++)
	{
		int x;cin>>x;
		while(tt&&stk[tt]>=x)//栈不空,且栈顶不小于当前数 
			tt--; 
		if(tt) cout<<stk[tt]<<" ";
		else cout<<"-1"<<" ";
		stk[++tt]=x;
	}
} 

最大子序和

135. 最大子序和 - AcWing

        滑动窗口维护最小的前缀和,由于是前缀和,窗口大小要扩大一位,且初始把0加入 

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m;
int w[N];
int q[N];
int res=-0x3f3f3f3f;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>w[i];
		w[i]+=w[i-1];
	}
	int hh=0,tt=-1;
	q[++tt]=0;//前缀和,s[0]=0  
	for(int i=1;i<=n;i++){//由于是前缀和,需要把窗口扩大一位 
		if(hh<=tt&&i-q[hh]+1>m+1) hh++;
		res=max(res,w[i]-w[q[hh]]);
		while(hh<=tt&&w[i]<=w[q[tt]]) tt--;
		q[++tt]=i;
	} 
	cout<<res;
} 


烽火传递

1089. 烽火传递 - AcWing

        f[i] 表示对于前i个烽火台,在第i个烽火台发信号的最小代价 

由于连续m个烽火台至少有一个发信号,当第i个烽火台发信号时,需要[i-m,i-1]之间至少有一个烽火台发信号,即i的前m个f[i]的最小值,单调队列需要维护的窗口大小为m+1

为了保证最后连续m个烽火台中有发信号的,答案需要在f[n-m+1,n]中取最小值

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,m;
int w[N];
int q[N];
int f[N];//f[i] 前i个,在i发信号,最小花费 
int hh=0,tt=-1;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>w[i];
	}
	q[++tt]=0; 
	//维护f[i]最小的 
	for(int i=1;i<=n;i++){
		if(hh<=tt&&i-q[hh]+1>m+1) hh++;
		f[i]=f[q[hh]]+w[i];
		while(hh<=tt&&f[i]<=f[q[tt]]) tt--;
		q[++tt]=i;
	}
	//保证最后m个中有发送的 
	int res=1e8;
	for(int i=n-m+1;i<=n;i++){
		res=min(res,f[i]);
	}
	cout<<res;
} 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vic.GoodLuck

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值