题目
https://www.luogu.org/problemnew/show/1886
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
分析
- 弄个单调队列,记录队列中元素的 id(在原数组中的下标) 以及数值。
- 先求最小值吧:
- 每次往队尾插入一个新元素 A 时,从队尾把比 A 大的全弹出来,保持队列单调。
- 再判断一下队首元素的 id 是否越过了(小于)边界,越过了就出队。
- 每次输出队首元素的数值即可。
- 求最大值同理。
- 时间复杂度 On。
程序
#include <cstdio>
struct zzk{
int v,id;} q[2000005];
int i,n,k,l,r,A,a[1000005];
int main(){
scanf("%d%d",&n,&k);
for (i=1; i<=n; i++) scanf("%d",&a[i]);
for (l=++r,i=1; i<=n; i++){
A=a[i];
if (i-q[l].id>=k) l++;
while (q[r].v>=A && r>=l) r--;
q[++r]=(zzk){A,i};
if (i>=k) printf("%d ",q[l].v);
}
for (puts(""),l=1,r=0,i=1; i<=n; i++){
A=a[i];
if (i-q[l].id>=k) l++;
while (q[r].v<=A && r>=l) r--;
q[++r]=(zzk){A,i};
if (i>=k) printf("%d ",q[l].v);
}
}
提示
- 这题也可以用线段树做,不过时间复杂度就多了个 log,练练手也可以。
- RMQ 也可以做呢。