单调队列

洛谷 P1886 滑动窗口(单调队列)

旧街凉风 提交于 2020-03-25 21:52:50
题目链接 https://www.luogu.org/problemnew/show/P1886 题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。 例如: The array is [1 3 -1 -3 5 3 6 7], and k = 3. 输入输出格式 输入格式: 输入一共有两行,第一行为n,k。 第二行为n个数(<INT_MAX). 输出格式: 输出共两行,第一行为每次窗口滑动的最小值 第二行为每次窗口滑动的最大值 输入输出样例 输入样例#1: 8 3 1 3 -1 -3 5 3 6 7 输出样例#1: -1 -3 -3 -3 3 3 3 3 5 5 6 7 说明 50%的数据,n<=10^5 100%的数据,n<=10^6 解题思路 先理解文意:对于给定一个长度为n的序列,找出所有长为k的区间的最大值(最小值)。 首先很多人会想到,枚举每一个长为k的区间,然后遍历一遍,找到最大值(最小值),这样的时间复杂度是o(nk)的, 显然超时 。 所以我们需要换一种思路。 单调队列 :单调队列就是一个一直保持单调性(递增或递减)的长度最大为k的双端队列。 我们维护这样一个单调队列,使它队首元素即为要求的最大值(最小值)。 所以本题的核心是:怎样维护一个单调队列。

洛谷省选——单调队列

隐身守侯 提交于 2020-03-18 13:55:12
T1 题意 花盆 给出n个点的坐标,以及时间差d。求区间内y坐标的最大值与最小值差大于等于d 的最小x轴区间长度。 (n<=1e5, d<=1e6, x,y<=1e6) ### 思路 设l为一段满足条件的区间的左端点,r为该区间最小右端点,则r具有单调性。 『 单调性证明: 设端点l1满足条件的最小右端点为r1, 端点l2(l2>=l1)满足条件的最小右端点为r2。 则: r2>=r1, 否则端点l1满足条件的最小右端点为r2,矛盾。 故: r具有单调性。 』 因此可在对n个点按x排序之后,枚举左端点,用单调队列维护满足条件的区间的y轴上的最大值与最小值。 代码 #include <bits/stdc++.h> #define I __inline__ __attribute((always_inline)) #define ri register int using namespace std; const int inf = 0x3f3f3f3f; const int maxn = 1e5 + 10; I char readc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } I int

【算法学习】单调队列

自闭症网瘾萝莉.ら 提交于 2020-03-18 13:54:47
基础模板 求区间最大值最小值之差在[L,R]范围内的最长区间长度 int l=1; int l1=1,r1=0; int l2=1,r2=0; int ans=0; for(int r=1;r<=n;r++){ //删除队尾元素再入队,使满足单调性 while(l1<=r1 && a[r]>a[mxq[r1]]){ r1--; } mxq[++r1]=r; while(l2<=r2 && a[r]<a[mnq[r2]]){ r2--; } mnq[++r2]=r; //队首元素只能用来维护max和min的最大差值 while(l<=r && l1<=r1 && l2<=r2 && a[mxq[l1]]-a[mnq[l2]]>k){ l++; //删去队头无效元素 while(l1<=r1 && mxq[l1]<l){ l1++; } while(l2<=r2 && mnq[l2]<l){ l2++; } } //根据最小差值的限制,更新答案 if(l1<=r1 && l2<=r2 && a[mxq[l1]]-a[mnq[l2]]>=m){ ans=max(ans,r-l+1); } } 单调队列常用作有大小限制,带删除操作的优先队列?可用来优化dp,或其他类似状态转移。 例题 HDU3530 即上面的模板题 HDU3706 题意 给定 \(n\) , \(A\) , \(B\)

单调队列优化dp+决策单调性——poj3017好题

无人久伴 提交于 2020-03-17 17:49:32
/* dp[i]表示前i个数的最优解 dp[i]=min{ dp[j]+max[j+1,i] },sum[i]-sum[j]<=M 使用决策单调性:显然dp[i]是单调递增的 那么维护一个区间[j+1,i]关于a的递减单调队列q 对于a[q[1]]可以控制的决策范围是[j+1,q[1]],又因为决策单调性,所以这一段区间的最优解就是dp[j+1]+a[q[1]] 同理,遍历整个队列,可得后面每一段的最佳决策 */ #include<iostream> #include<queue> #include<cstdio> using namespace std; #define ll long long #define N 200005 ll n,M,a[N],sum[N],dp[N]; int main(){ cin>>n>>M; for(int i=1;i<=n;i++)scanf("%lld",&a[i]); for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i]; for(int i=1;i<=n;i++)if(a[i]>M){ puts("-1");return 0; } memset(dp,0x3f,sizeof dp);dp[0]=0; deque<int>q; deque<int>::iterator it,itt; int pos=1;

[CSP day2T2]划分

∥☆過路亽.° 提交于 2020-03-11 05:53:10
划分 题解 本题很明显的一道dp题。 我们可以先写出一个朴素的dp, 暴力就不分析了 , 表示将j+1-i的划为一段的最小值。很明显,这是一个 的算法,只能得到32分。我们需要考虑一下dp的优化,一眼单调队列 (笔者也只想得到这个) 。我们将划分点定下来,先将所有在它前面的塞入队列,再更新再划分点后面的点,就可以做到 ,有64分了。 之后是一个十分重要的结论: ,这其实通过单调队列分析得到。 接下来是证明, 请别怪笔者能力有限,复制证明,笔者也没证出来 。 引用自: http://matthew99.blog.uoj.ac/archive 结论 设最优解的倒数第 段的开始位置为 ,那么对于所有的满足段和不递减条件的解,一定有下列条件之一: 该解不存在 段。 该解从右到左数第 段的开始位置至少为 。 这里我们假设最优解的倒数最后一段(也就是第一段)的开始位置是1,所以上述条件蕴含没有任何解的段数比最优解要多。 换句话说,如果你把所有解的断点从大到小写下来,然后剩下的位置补0,那么最优解对应的序列在所有位置都是最大值。 同时容易注意到满足这个结论中条件的解一定唯一,因此最优解释良定义的。 根据结论推出的线性做法 设pipi表示以ii结尾的前缀,最后一段的位置的最大值,那么pipi一定是满足 的最大的 。 这个非常显然,因为 对应的是每个位置结尾的前缀最后一段的最小和,如果不存在

?DP_ 单调队列优化 DP_理想正方形

泄露秘密 提交于 2020-02-27 19:03:05
其实对于这道题和上一道旅行问题我都没有怎么看出DP的感觉在哪里,但是确实可以加深对于单调队列这个神奇数据结构的理解,这道题本质上是一道二维的“滑动窗口”求最值的问题。 题目 : 理想的正方形 做法 :分解成一维的“滑动窗口”来处理。 首先对每一行做该算法,然后将最大(小)值记录到最右,之后对每一(处理后的)列(大于等于n)做算法处理,最后更新最小值即可。时间复杂度为 O ( n 2 ) O(n^2) O ( n 2 ) . 代码: # include <iostream> # include <cstdio> # include <cstring> # include <algorithm> using namespace std ; const int N = 1010 , INF = 0x3f3f3f3f ; int w [ N ] [ N ] ; int maxr [ N ] [ N ] , minr [ N ] [ N ] ; int n , m , k ; void get_min ( int a [ ] , int b [ ] , int len ) { int hh = 0 , tt = - 1 ; int q [ N ] ; for ( int i = 1 ; i <= len ; i ++ ) { if ( hh <= tt && q [ hh ] <= i -

单调队列

会有一股神秘感。 提交于 2020-02-26 22:22:08
参考文献: https://www.jianshu.com/p/e59d51e1eef5 单调队列,顾名思义,是一种具有单调性的队列。众所周知,单调性有单调递增和单调递减两种,相应的单调队列也分为单调递增队列和单调递减队列两种。 单调递增队列 :保证队列头元素一定是当前队列的最小值,用于维护区间的最小值。 单调递减队列 :保证队列头元素一定是当前队列的最大值,用于维护区间的最大值。 实现单调队列,主要分为三个部分: 去尾操作 : 队尾元素出队列 。当队列有新元素待入队,需要从队尾开始,删除影响队列单调性的元素,维护队列的单调性。(删除一个队尾元素后,就重新判断新的队尾元素) 去尾操作结束后,将该新元素入队列。 删头操作 : 队头元素出队列 。判断队头元素是否在待求解的区间之内,如果不在,就将其删除。(这个很好理解呀,因为单调队列的队头元素就是待求解区间的极值) 取解操作 :经过上面两个操作,取出 队列的头元素 ,就是 当前区间的极值 。 代码: // 假设有 n 个元素的序列,要求解的是长度为 k 的区间的最大值 // 队列que是STL的双向队列deque // 队列存放的是元素在序列中的序号 deque<int>que;// 双向队列 for(int i=1;i<=n;i++) { while(!que.empty() && a[que.back()]<a[i]) { que

滑动窗口(单调队列)

≯℡__Kan透↙ 提交于 2020-02-13 00:49:21
给定一个大小为n≤106的数组。 有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。 您只能在窗口中看到k个数字。 每次滑动窗口向右移动一个位置。 以下是一个例子: 该数组为[1 3 -1 -3 5 3 6 7],k为3。 窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 7 -1 3 1 [3 -1 -3] 5 3 6 7 -3 3 1 3 [-1 -3 5] 3 6 7 -3 5 1 3 -1 [-3 5 3] 6 7 -3 5 1 3 -1 -3 [5 3 6] 7 3 6 1 3 -1 -3 5 [3 6 7] 3 7 您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。 输入格式 输入包含两行。 第一行包含两个整数n和k,分别代表数组长度和滑动窗口的长度。 第二行有n个整数,代表数组的具体数值。 同行数据之间用空格隔开。 输出格式 输出包含两个。 第一行输出,从左至右,每个位置滑动窗口中的最小值。 第二行输出,从左至右,每个位置滑动窗口中的最大值。 输入样例: 8 3 1 3 -1 -3 5 3 6 7 输出样例: -1 -3 -3 -3 3 3 3 3 5 5 6 7 完整代码: #include <iostream> #include <cstring> #include <queue> using namespace std; const

洛谷 P2300 合并神犇

风格不统一 提交于 2020-02-11 18:57:56
\(\quad\) 看到这道题,觉得和 P5665 划分 很像,当时考场上我写的就是类似一些题解的 \(O(n^2)\) 算法,完美卡成 \(64\) 分。 \(\quad\) 我详细介绍一下 \(O(n)\) 的单调队列优化算法,也当作是自己学习单调队列的总结吧。 \(\quad\) 单调队列,顾名思义,具有单调性。它本质上是一个双端队列,队头代表当前的最优解(这一点很像优先队列)。 for(int i = 1; i <= n; ++i){ while(!q.empty() && out_of_date(q.front())) q.pop_front(); while(!q.empty() && notBetterThan(q.back(), i)) q.pop_back(); q.push_back(i); operate(q.front()); } \(\quad\) 结合这一份样例代码分析。为了便于理解,这里使用 STL 中的 deque 充当载体。 \(\quad\) 第一个 while 语句每次将处于队列前端的一些元素弹出队列:这些元素虽然最优,然而已经“过期”了,因而必须被排除。由于每个元素的“过期时间”具有单调性,即处于前面的元素一定比后面的更早“过期”,这一操作可以保证所有“过期”元素都被排除。 \(\quad\) 第二个 while

【OI】单调队列

随声附和 提交于 2020-02-09 09:06:08
所谓单调队列,就是一个保持着某种性质的队列,通常是队列从队头到队尾,维护一种递增递减的关系。 这种队列通常用来解决一些连续区间的最值问题。 这种队列的入队要保证符合当前的性质,例如一个递增的单调序列(从左到右是从头到尾):7,9,10,11 这时,入队时要保证是递增的:例如12这个元素可以入队,而10这个元素不行。 这时我们看队头,这就是整个队列中最小的元素。所以,递增的队列可以维护最小值;递减的队列可以维护最大值。 当然,实现一个单调队列需要看题目要求,通常需要对队头和队尾进行更新。 例如这道题: P1886 滑动窗口 题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。 这道题可以使用单调队列来做。 在滑动窗口的时候,改变只有窗口序列中的第一个元素和最后一个元素,所以我们可以把一开始的序列同样的放到两个单调队列中,一个递增,一个递减,分别维护最小值和最大值。 用求最小值(递增单调队列)做例子,我们对每个元素运行这样一个判断: 1.如果当前元素<队尾,那么删除队尾。不停执行该操作,直到队为空且没有可以删除的队尾为止。 2.在队尾加上当前元素。 3.如果队头离开了窗口的范围,那么删除队头。 求最大值的操作类似。 根据上题,我们可以总结出单调队列一般有这样的步骤: 1