单调队列
前置:
队列的定义
队列 是仅限在 一端 进行 插入,另一端 进行 删除 的 线性表。
队列 又被称为 先进先出 (First In First Out) 的线性表,简称 FIFO 队列。
队首
允许进行元素删除的一端称为 队首。
队尾
允许进行元素插入的一端称为 队尾。
数据入队
队列的插入操作,叫做 入队。它是将 数据元素 从 队尾 进行插入的过程
数据出队
队列的删除操作,叫做 出队。它是将 队首 元素进行删除的过程
清空队列
队列的清空操作,就是一直 出队,直到队列为空的过程,当 队首 和 队尾 重合时,就代表队尾为空了
获取队首数据
对于一个队列来说只能获取 队首 数据
双端队列的定义
双端队列 是一种具有 队列 和 栈 的性质的数据结构,是我们常说的 deque(double-ended queue),是一种限定 插入 和 删除 操作在表的两端进行的线性表。这两端分别被称为 队首 和 队尾。
队首入队
队列的插入操作,叫做 入队。
队首入队 就是将 数据元素 从 队首 进行插入的过程。
队尾入队
队尾入队 就是将 数据元素 从 队尾 进行插入的过程。
队首出队
队列的删除操作,叫做 出队。队首出队 是将 队首 元素进行删除的过程。
队尾出队
队尾出队 是将 队尾 元素进行删除的过程
单调队列
“如果一个选手比你小还比你强,你就可以退役了。”——单调队列的原理
单调队列是一种主要用于解决滑动窗口类问题的数据结构,即,在长度为 n 的序列中,求每个长度为 m 的区间的区间最值。它的时间复杂度是 O(n) .
代码:
求区间最大值
1 | deque<int> Q; // 存储的是编号 |
滑动窗口 /【模板】单调队列
题目描述
有一个长为 \(n\) 的序列 \(a\),以及一个大小为 \(k\) 的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。
例如,对于序列 \([1,3,-1,-3,5,3,6,7]\) 以及 \(k = 3\),有如下过程:
\[\def\arraystretch{1.2} \begin{array}{|c|c|c|}\hline \textsf{窗口位置} & \textsf{最小值} & \textsf{最大值} \\ \hline \verb![1 3 -1] -3 5 3 6 7 ! & -1 & 3 \\ \hline \verb! 1 [3 -1 -3] 5 3 6 7 ! & -3 & 3 \\ \hline \verb! 1 3 [-1 -3 5] 3 6 7 ! & -3 & 5 \\ \hline \verb! 1 3 -1 [-3 5 3] 6 7 ! & -3 & 5 \\ \hline \verb! 1 3 -1 -3 [5 3 6] 7 ! & 3 & 6 \\ \hline \verb! 1 3 -1 -3 5 [3 6 7]! & 3 & 7 \\ \hline \end{array} \]
输入格式
输入一共有两行,第一行有两个正整数 \(n,k\)。 第二行 \(n\) 个整数,表示序列 \(a\)
输出格式
输出共两行,第一行为每次窗口滑动的最小值
第二行为每次窗口滑动的最大值
样例 #1
样例输入 #1
1 | 8 3 |
样例输出 #1
1 | -1 -3 -3 -3 3 3 |
提示
【数据范围】
对于 \(50\%\) 的数据,\(1 \le n \le 10^5\);
对于 \(100\%\) 的数据,\(1\le k \le n \le 10^6\),\(a_i \in [-2^{31},2^{31})\)。
1 |
|
单调队列优化dp
当我们为了实现给动态规划的复杂度降维的时候,通常就需要单调栈/队列,通常用来维护前面状态下可以取到的最大值或者最小值,然后直接进行转移。
1 |
|
对于常规多重背包,f[i][j]代表体积为j,选到第i个物品的最大价值。由于一种物体数量可以多个,故需要枚举数量。
1 | f[i][j]=max( f[i-1] [j-k*v[i]] +k*w[i] ) 其中0<=k<=cnt[i] |
其实是有二进制拆分做法的
1 | for(int i=1;i<=n;i++) |
但今天的主题是单调队列
接下来开始进行推导过程
由:
1 | f[i][j]=max( f[i-1] [j-k*v[i]] +k*w[i] ) 其中0<=k<=cnt[i] |
cnt[i]代表这一种物体数目,w[i]是价值(wealth), v[i]是体积
我们设 j=k1*v[i] +d , k1=j/v[i] ,d=j%v[i] 前者是j能装下的最大个数,后者是余数
1 | f[i][j]= max( f[i-1][ k1*v[i]+d -k*v[i]] + k*w[i] ) 0<=k<=cnt[i] |
把j代换成了 k1*w[i] +d
1 | f[i][j]= max( f[i-1][ (k1-k)* v[i]+d] -(k1 -k) *w[i] ) + k1*w[i] 0<=k<=cnt[i] |
0<=k<=cnt[i]
令(k1-k)-->k
1 | f[i][j]= max( f[i-1][ k* v[i]+d] -k *w[i] ) + k1*w[i] k1-cnt[i]<= k <=k1 |
代码如下:
1 | for(int d=0;d<v;d++){ |