最近做了一个滑动窗口得题;然后就找一些来做了;
套着模板做勒,不容易啊;
看别人的解释套用一下
那么如何用队列处理呢?先简单说一下解题思路,以求最小值为例:一开始数组下标i为0,我们向队列里不停的放元素,并且一直保持队首位元素为最小值,直到第k个数,此时队首元素即为前k个数的最小值。然后我们把队首输出。继续往下走,走的过程中把队列中下标超出(i-k+1)~i区间范围的数踢掉,继续保持队首元素为区间内最小值,然后输出队首元素。
简单归纳一下,对于每一次循环,我们要做的就是:先踢掉超出区间范围的元素,放入元素并保证队首为数组中当前区间的最小值,输出队首,往复。队列内储存的就是放入元素之前区间内单调递增的最小值序列。
那么我们要解决的问题有两个:
1. 如何去除超出区间范围的元素:这就是代码中node节点的作用了,node节点中储存了原数组的下标,对于每一次新输入的第i个数,因为单调队列内元素位置是从前到后的,我们只要将队首元素的node.order和i进行判断,看此时是否i-node.order>=k即可,是就让队首元素出列。
2. 如何保证队首元素为区间内最小值:对于某一区间,我们每输入一个数,就看看队尾元素的值是否比它大,是就让这个元素出列,直到队列为空或者队尾元素值比这个数小,然后我们把这个数放进去,这个时候队首元素即为区间最小值,输出即可。
合理性分析:从队列为空开始,我们放入第0个数,一直到k-1,每一次输入的时候都让队尾值比当前输入值小的出列,很容易知道此时队首就是[0,k-1]的最小值。那么接下来输入第k个数,可以肯定的是区间[1,k]的最小值与[0,k-1]的最小值有关。有两种情况,一是[1,k]的最小值来自k前面的数,这种情况下,我们看[0,k-1]的情况,若[0,k-1]的最小值来自第0位,就说明队列里肯定有除第0个元素外的元素,即队列里肯定有[1,k-1]的最小值序列,且它们是单调递增的。此时由于第0位超出区间范围,出列,不影响区间[1,k]最小值的查找。若[0,k-1]的最小值来自[1,k-1],自然不影响[1,k]最小值的查找。二是最小值就是k,那么进行操作即可把队列清空,再把它放进即可,此时队首就是k,即最小值。对于一般性的情况,同样有上述关系。
要注意的点:
1. 写if和while的时候一定要先写判断队列是否为空,否则if和while中会进行溢出操作。
2. 判断队首是否出队和队列长度没有确定关系,因为队列不一定都包含了整个区间的元素。
3. 对于重复元素去不去除都可以,去除可以保证队列中至少有一个,不去除不影响队首,且在遇到更小的值时都会出队。
4. 这个算法只有一个for循环,且每个元素都只出入一次,所以时间复杂度为o(n)。
————————————————
版权声明:本文为CSDN博主「Gaoithe」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sinat_40471574/article/details/90577147
P1440 求m区间内的最小值
题目描述
一个含有 nn 项的数列,求出每一项前的 mm 个数到它这个区间内的最小值。若前面的数不足 mm 项则从第 11 个数开始,若前面没有数则输出 00。
输入格式
第一行两个整数,分别表示 nn,mm。
第二行,nn 个正整数,为所给定的数列 a_iai。
输出格式
nn 行,每行一个整数,第 ii 个数为序列中 a_iai 之前 mm 个数的最小值。
输入输出样例
输入 #1
6 2 7 8 1 4 3 2
输出 #1
0 7 7 1 1 3
说明/提示
对于 100\%100% 的数据,保证 1\le m\le n\le2\times10^61≤m≤n≤2×106,1\le a_i\le3\times10^71≤ai≤3×107。
#include<bits/stdc++.h>
using namespace std;
struct node
{
int id;
int va;
}a[1000005];
deque<node>q;
int main()
{
int t;
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&t);
if(i==0)
cout<<0<<endl;
else
printf("%d\n",q.front().va);
if(!q.empty()&&i-q.front().id>=m)
q.pop_front();
while(!q.empty()&&q.back().va>t)
q.pop_back();
a[i].id=i;
a[i].va=t;
q.push_back(a[i]);
}
}
之前牛客的滑动窗口
https://blog.csdn.net/qq_49638570/article/details/115480354
又找过了一个模板
滑动窗口
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给一个长度为N的数组,一个长为K的滑动窗体从最左端移至最右端,你只能看到窗口中的K个数,每次窗体向右移动一位,如下图:
你的任务是找出窗体在各个位置时的最大值和最小值。
输入描述:
第1行:两个整数N和K;
第2行:N个整数,表示数组的N个元素(≤2 \times10^9≤2×10
9
);
输出描述:
第一行为滑动窗口从左向右移动到每个位置时的最小值,每个数之间用一个空格分开;
第二行为滑动窗口从左向右移动到每个位置时的最大值,每个数之间用一个空格分开。
示例1
输入
8 3
1 3 -1 -3 5 3 6 7
输出
-1 -3 -3 -3 3 3
3 3 5 5 6 7
备注:
对于20 %20%的数据,K≤N≤1000;
对于50 %50%的数据,K≤N≤10^5K≤N≤10^5;
对于100 %100%的数据,K≤N≤10^6K≤N≤10^6。
#include<bits/stdc++.h>
using namespace std;
struct node
{
int id;
int va;
}a[1000005];
int c[1000005];
deque<node>q,p;
int main()
{
int k=0;
int n,m;
scanf("%d%d",&n,&m);
int t;
for(int i=0;i<n;i++)
{
scanf("%d",&t);
if(!q.empty()&&i-q.front().id>=m)
q.pop_front();
if(!p.empty()&&i-p.front().id>=m)
p.pop_front();
while(!q.empty()&&t>q.back().va)
q.pop_back();
while(!p.empty()&&t<p.back().va)
p.pop_back();
a[i].id=i;
a[i].va=t;
q.push_back(a[i]);
p.push_back(a[i]);
if(i>=m-1)
{
c[k++]=q.front().va;
printf("%d ",p.front().va);
}
}
cout<<endl;
for(int i=0;i<k;i++)
{
printf("%d ",c[i]);
}
}
P2032 扫描
题目描述
有一个 1 \times n1×n 的矩阵,有 nn 个整数。
现在给你一个可以盖住连续 kk 个数的木板。
一开始木板盖住了矩阵的第 1 \sim k1∼k 个数,每次将木板向右移动一个单位,直到右端与第 nn 个数重合。
每次移动前输出被覆盖住的数字中最大的数是多少。
输入格式
第一行两个整数 n,kn,k,表示共有 nn 个数,木板可以盖住 kk 个数。
第二行 nn 个整数,表示矩阵中的元素。
输出格式
共 n - k + 1n−k+1 行,每行一个整数。
第 ii 行表示第 i \sim i + k - 1i∼i+k−1 个数中最大值是多少。
输入输出样例
输入 #1
5 3 1 5 3 4 2
输出 #1
5 5 4
说明/提示
对于 20\%20% 的数据,1 \leq k \leq n \leq 10^31≤k≤n≤103。
对于 50\%50% 的数据,1 \leq k \leq n \leq 10^41≤k≤n≤104。
对于 100\%100% 的数据,1 \leq k \leq n \leq 2 \times 10^61≤k≤n≤2×106,矩阵中的元素大小不超过 10^4104 并且均为正整数。
#include <bits/stdc++.h>
using namespace std;
struct node
{
int id;
int va;
}a[1000005];
deque<node>q;
int main()
{
int t;
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&t);
if(!q.empty()&&i-q.front().id>=m)
{
q.pop_front();
}
while(!q.empty()&&q.back().va<t)
{
q.pop_back();
}
a[i].id=i;
a[i].va=t;
q.push_back(a[i]);
if(i>=m-1)
{
printf("%d\n",q.front().va);
}
}
}
题目描述
今天是小Z的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值。
小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大,但小Z最多又只能吃M小块(M≤N)的蛋糕。
吃东西自然就不想思考了,于是小Z把这个任务扔给了学OI的你,请你帮他从这N小块中找出连续的k块蛋糕(k≤M),使得其上的幸运值最大。
输入格式
输入文件cake.in的第一行是两个整数N,M。分别代表共有N小块蛋糕,小Z最多只能吃M小块。
第二行用空格隔开的N个整数,第i个整数Pi代表第i小块蛋糕的幸运值。
输出格式
输出文件cake.out只有一行,一个整数,为小Z能够得到的最大幸运值。
输入输出样例
输入 #1
5 2 1 2 3 4 5
输出 #1
9
输入 #2
6 3 1 -2 3 -4 5 -6
输出 #2
5
说明/提示
对20%的数据,N≤100。
对100%的数据,N≤500000,|Pi|≤500。 答案保证在2^31-1之内。
#include<bits/stdc++.h>
using namespace std;
int b[1000005];
int sum[1000005];
struct node
{
int id;
int va;
}a[1000005];
deque<node>q;
int main()
{
int ans;
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&b[i]);
if(i==0)
sum[0]=b[i];
else
sum[i]+=sum[i-1]+b[i];
if(!q.empty()&&i-q.front().id>m)
{
q.pop_front();
}
while(!q.empty()&&q.front().va>sum[i])
{
q.pop_back();
}
a[i].id=i;
a[i].va=sum[i];
q.push_back(a[i]);
if(i==0)
{
ans=sum[i];
}
else
if(i<m)
ans=max(ans,sum[i]);
else
ans=max(ans,sum[i]-q.front().va);
}
printf("%d\n",ans);
}