题目大意:
给你一个长度为N的数组,一个长为K滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:
Window position | Min value | Max value |
[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 |
你的任务是找出窗口在各位置时的Max value ,Min value。
输入格式:
第一行n,k,第二行长度为n的数组
输出格式:
第一行每个位置的Min value,第二行每个位置的Max value
样例:
Window.in
8 3
1 3 -1 -3 5 3 6 7
Window.out
-1 -3 -3 -3 3 3
3 3 5 5 6 7
数据范围:
20%:n≤500;
50%:n≤100000;
100%:n≤1000000;
时间限制:
12000ms
RMQ问题,线段树代码量太大于是用单调队列实现- -
单调队列具有队列内所有元素不是单调递增就是单调递减的性质,所以每次的最小(最大)值一定会在队首
程序实现过程中先将前k个元素入队,此后每次在队尾加入a[k+1...n],在插入元素中同时进行以下操作:
1、将队尾所有大于a[i]的值弹出队列
2、插入a[i]到队尾
3、判断队首元素位置是否超出i-k
注意:
1、在更新队列元素时要同时记录该元素在原数据的位置
2、在进行操作1时要用二分优化(可以用C++编译器卡时间AC,但换成G++和GCC就会TLE)
代码如下:
#include<cstring>
#include<cstdio>
#define N 1000050
#define INF 2147483647
using namespace std;
int n,k;
int a[N];
struct Elem{
int k,num;
}Queue[N];
int l=1,r=1;
inline void GetMin(){
memset(Queue,0,sizeof Queue);
Queue[0].k=-INF;
l=1,r=1;
for(int i=1;i<=k;i++){
while(Queue[r].k>=a[i] && r>=l) r--;
Queue[++r].k=a[i];
Queue[r].num=i;
}
for(int i=k;i<=n;i++){
while(Queue[r].k>=a[i] && r>=l) r--; //维护单调性
Queue[++r].k=a[i];
Queue[r].num=i;
while(Queue[l].num<=i-k) l++; //维护队列下标范围k以内
printf("%d ",Queue[l].k);
}
}
inline void GetMax(){
memset(Queue,0,sizeof Queue);
Queue[0].k=INF;
l=1,r=1;
for(int i=1;i<=k;i++){
while(Queue[r].k<=a[i] && r>=l) r--;
Queue[++r].k=a[i];
Queue[r].num=i;
}
for(int i=k;i<=n;i++){
while(Queue[r].k<=a[i] && r>=l) r--;
Queue[++r].k=a[i];
Queue[r].num=i;
while(Queue[l].num<=i-k) l++;
printf("%d ",Queue[l].k);
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",a+i);
GetMin();
printf("\n");
GetMax();
return 0;
}