首页 > 其他分享 >单调队列学习笔记

单调队列学习笔记

时间:2024-02-27 09:46:32浏览次数:15  
标签:窗口 队列 元素 long 笔记 数组 单调

What Is Monotonic Queue

单调队列是一种特殊的队列数据结构,用于维护一定的单调性,通常是单调递增或单调递减。

单调队列的主要特点是,队列中的元素满足特定的单调性要求,使得队列的头部元素(或者尾部元素,取决于具体问题)始终是当前队列中的最大(或最小)值。这种特性使得单调队列可以高效地处理一些需要在不断变化的窗口或序列中找到最大(或最小)值的问题。

Monotonic Queue Can Do What

单调队列在解决一些与窗口和单调性有关的问题时非常有用。以下是一些可以使用单调队列解决的常见问题:

  1. 滑动窗口最大/最小值: 给定一个数组和一个固定大小的窗口,需要在窗口在数组上滑动的过程中,快速找到每个窗口的最大或最小值。

  2. 连续子数组的平均值大于阈值的个数: 给定一个数组和一个阈值,找到所有长度为固定值的连续子数组,使得子数组的平均值大于给定的阈值。

  3. 下一个更大元素: 给定一个数组,对于每个元素,找到在其右边第一个比它大的元素。

这些问题在实际应用中非常常见,而单调队列可以帮助有效解决这些问题,因为它们可以在O(N)的时间复杂度内进行操作,而不是传统的O(N^2)解法。通过维护单调性,单调队列可以在滑动窗口或者数组遍历的过程中高效地找到满足特定条件的元素。

How To Solve These Question

1.滑动窗口最大/最小值

例题:- P1886 滑动窗口 /【模板】单调队列

这题之前使用线段树做的,但是\(nlogn>n\),1.55s>1.33s。用单调队列整整快了0.22s也不是很多

当使用单调队列来解决滑动窗口最大/最小值问题时,需要维护一个滑动窗口,以及一个单调递减队列。单调队列用于存储当前窗口内的元素的下标(因为要保持单调队列元素在窗口内),使得队列的头部始终是窗口内的最值元素。以下是解决滑动窗口最大值问题的方法:

假设有一个数组 \(arr\) 和一个窗口大小 k,我们需要找到每个窗口的最大值。

创建一个单调递减队列 deque,用于存储元素在窗口中的下标。

遍历数组,对于每个元素 \(i\) 进行以下操作:

在每个新元素要加入窗口时,从队列尾部开始,将所有比新元素小的元素下标出队,以保持队列的单调递减性质。
将当前元素下标入队到队列尾部。
对于每个窗口的起始位置,计算窗口内的最大值并记录。

#include<bits/stdc++.h>
using namespace std;
long long n,k,a[1000005];
deque<int>q;//双端队列
int main(){
	cin>>n>>k;
	for(long long i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		while(!q.empty()&&q.front()<i-k+1){//保证元素在窗口内
            q.pop_front();
        }
        while(!q.empty()&&a[i]<=a[q.back()]){//保证队列内单调递增(队首为区间最小值)
            q.pop_back();
        }
        q.push_back(i);
		if(i>=k) cout<<a[q.front()]<<" ";
	}
	cout<<endl;
	while(!q.empty()){//同上
		q.pop_front();
	}
	for(int i=1;i<=n;i++){
		while(!q.empty()&&q.front()<i-k+1){
            q.pop_front();
        }
        while(!q.empty()&&a[i]>=a[q.back()]) {
            q.pop_back();
        }
        q.push_back(i);
		if(i>=k) cout<<a[q.front()]<<" ";
	}
	
	return 0;
}

2.连续子数组的平均值大于阈值的个数

Leetcode 1343

本人没有leetcode账号qwq,只能看题面了

因为滑动窗口长度为k,所以用双端队列即可

#include<bits/stdc++.h>
using namespace std;
int n,a[1000005],k,t,sum=0,ans=0;
deque<int>q;
int main(){
	cin>>n>>k>>t;
	t=k*t;//平均值>=t -> 子数组数的和>=k*t
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		while(!q.empty()&&q.front()<i-k+1){//超出范围就pop
			sum-=a[q.front()];
			q.pop_front();
		}
		q.push_back(i);
		sum+=a[i];
		if(sum>=t) ans++;//看看可不可以
	}
	cout<<ans<<endl;

	return 0;
}

3.下一个更大元素

例题: P1901 发射站

XJOI上 $ n^{\smash{2}} $ 过百万

假设我们有一个数组 arr,我们要为每个元素找到在其右边第一个比它大的元素。我们可以使用一个单调递减队列来解决这个问题。队列中的元素按照从大到小的顺序排列,当我们遍历数组时,如果当前元素大于队列末尾的元素,那么我们就可以得到队列末尾元素的下一个更大元素。

1.创建一个空的deque

遍历数组,对于每个元素执行以下操作:

如果队列不为空,并且当前元素大于等于队列末尾的元素,那么说明队列末尾的元素找到了下一个更大元素。我们将队列末尾的元素弹出,并将其下标与当前元素建立映射,表示下一个更大元素的位置。
将当前元素的下标入队到队列中,以便稍后可以根据下标获取元素。
遍历完成后,队列中剩余的元素表示没有找到下一个更大元素的位置,可以标记为-1或数组长度。

#include<bits/stdc++.h>
using namespace std;
long long n,h[1000005],v[1000005],ans[1000005];
void getmax() {
    stack<long long>stk;//单调栈
    for (long long i=1;i<=n;i++){//对于每一个i进行操作
        while(!stk.empty()){
			if(h[i]<=h[stk.top()]) break;//不符合要求
            long long j=stk.top();
            if(!stk.empty()){
                ans[i]+=v[j];//h[j]<h[i] -> j向i发射
            }
			stk.pop();
        }
        if(!stk.empty()) ans[stk.top()]+=v[i];//没有别的高就是i向stk.top()发射
        stk.push(i);
    }
}
int main(){
    cin>>n;
    for(long long i=1;i<=n;i++){
    	cin>>h[i]>>v[i];
	}
	getmax();
    long long maxx=-1;
    for(long long i=1;i<=n;i++){
    	maxx=max(maxx,ans[i]);
	}
	cout<<maxx<<endl;

    return 0;
}

标签:窗口,队列,元素,long,笔记,数组,单调
From: https://www.cnblogs.com/wuhupai/p/18036194

相关文章

  • 前中后缀表达式学习笔记
    前言表达式是数学和计算机编程中常见的概念,用于表示运算和计算过程。前缀、中缀和后缀表达式都是不同的方式来表示数学表达式,它们在计算机科学和计算器设计中都有一定的应用。中缀表达式(InfixExpression):这是最常见的数学表达式表示方法,也是人们通常在书写数学公式时使用的方式......
  • 树链剖分学习笔记
    树链剖分学习笔记树链剖分的思想及能解决的问题树链剖分用于将树分割成若干条链的形式,以维护树上路径的信息。具体来说,将整棵树剖分为若干条链,使它组合成线性结构,然后用其他的数据结构维护信息。树链剖分(树剖/链剖)有多种形式,如重链剖分,长链剖分和用于Link/cutTree的剖分(......
  • 组合数学学习笔记
    组合数学及相关计数法一、计数原理1.加法原理举个例子:从甲地到乙地共有海陆空三种选择,坐船有$3$班,坐车有$5$班,坐飞机有$2$班,问从甲地到乙地共有几种选择?解:这是一个幼儿园的题$3+5+2=10$加法原理(分类计数原理):完成一件事共有$n$类方法,第一类有$a_1$种方案,第二类有$......
  • 第十章 通过汇编语言了解程序的实际构成 笔记
    编语言是介于机器语言和高级编程语言之间的一种语言。它使用助记符来表示CPU指令,这些助记符相较于机器语言的二进制编码更为人类可读。虽然汇编语言比高级语言更难以编写和理解,但它能够提供对程序行为的直接控制,以及与计算机硬件架构密切相关的通过学习汇编语言,我们可以了解程序......
  • 【机器学习科学库】全md文档笔记:Matplotlib详细使用方法(已分享,附代码)
    本系列文章md笔记(已分享)主要讨论人工智能相关知识。主要内容包括,了解机器学习定义以及应用场景,掌握机器学习基础环境的安装和使用,掌握利用常用的科学计算库对数据进行展示、分析,学会使用jupyternotebook平台完成代码编写运行,应用Matplotlib的基本功能实现图形显示,应用Matplotlib......
  • CTF之RCE笔记
    https://www.ctfhub.com/#/skilltree命令注入命令注入,查看当前文件夹结构?ip=127.0.0.1;ls查看php文件内容?ip=127.0.0.1;cat11001571914029.php提交flag,成功破解过滤cat命令注入,查看当前文件夹结构?ip=127.0.0.1;ls使用cat尝试读取php文件?ip=127.0.0.1;catfla......
  • 【学习笔记】树型DP学习笔记
    学习笔记索引省流:被吊打了自己开的一个坑,死也要填完它。希望我随手写下的笔记对您的学习有所帮助(也不太可能)。更改日志2024/01/08:开坑,写了树的直径和换根DP,写不动了(((2024/01/08晚上:更新了最小点覆盖和最大独立集,看来精神还可以,顶着明天做手术的风险2024/01/09:修改错误+增补......
  • 【学习笔记】倍增ST表、LCA学习笔记
    学习笔记索引众所周知,scy5赛时在P10059Choose写了个滑动窗口骗\(40\)分,但是狂WA不止,丢掉了\(rk155\),于是就有了下面这两张口吐芬芳的图:听说这题可以用ST表做,但他不会,于是他就来学倍增乐。省流:被吊打了更改日志2024/01/16:开坑。倍增原理设做一件事有\(n\)个步骤。......
  • 【学习笔记】分块学习笔记
    学习笔记索引分块经常听别人提起,我也学一下。正片分块就是将一个数列分成很多块,然后每块单独操作,最后的结果放到原数列里。分块的题目类型经常是区间中修改和查询。这里,一个长度为\(n\)的数列最多分成\(\sqrtn\)个块。先来看例题吧。例题P2357守墓人题目背景在一......
  • 决策单调性相关
    1.四边形不等式1.1小标题该怎么起阿考虑一个形式如下的DP:\[f_{i}=\min_{j=1}^{i}val(j,i)\]其中\(i\)满足\(1\leqi\leqn\)。设\(f_{i}\)的最优决策点为\(oper_{i}\)。1.2一些概念决策单调性:指满足\(\symbfit{oper_1\leqoper_2\leq\dots\leqoper_......