Time Limit: 12000MS | Memory Limit: 65536K | |
Total Submissions: 43271 | Accepted: 12790 | |
Case Time Limit: 5000MS |
Description
The array is [1 3 -1 -3 5 3 6 7], and k is 3.
Window position | Minimum value | Maximum 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 |
Your task is to determine the maximum and minimum values in the sliding window at each position.
Input
Output
Sample Input
8 3 1 3 -1 -3 5 3 6 7
Sample Output
-1 -3 -3 -3 3 3 3 3 5 5 6 7
单调队列:
即队列中元素保持单调不减或单调不增,比如1 2 5 5 8 9就是单调非减序列,9 8 5 5 2 1就是单调非增序列,单调队列支持从队头和队尾删除,只能从队尾插入元素,在c++ STL中有一个deque就是可以用来实现这一个功能。deque是栈和队列的合体。当我们求一个序列区间的最大值和最小值,单调队列的队头即是所求的最大值和最小值,那么怎么来维护一个单调队列呢,以求区间最小值为例,我们知道队头元素便是我们要求的最小值,那么以题目中的样例来说,1 3 -1 -3 5 3 6 7,比如我要查询区间[1,8]的最小值,那么我可以逐个插入队列,但是由于我们要遵循单调队列的原则,所以一开始我们插入一个1,接着我们插入一个3,3 > 1所以队头不变,还是1,继续看,插入个-1,问题来了,-1 < 1,那么我们必须让-1当队头,但是现在-1在队尾,怎么让他变成队头呢,简单,删除1 和 3,-1就成为新的队头的,接下来读者就懂了,-3来了,-1就要被删除,依次类推,我们知道-3就是这个队列在区间上[1,8]的最小值,再回到本题,本题只是把查询范围变成了k罢了,就是查询的范围是实时更新的,所以本题要注意的是查询区间会变,所以不管队头是不是整个序列的最小值,不在查询序列当中我们就要把他删除,例如第一个元素是当前最小值,我查询的区间k为3,我查询到第4个元素的时候1还是最小值,但是已经不满足在当前区间了,那么我们就要把队头1删除,换做是区间[2,4]的最小值。
代码:
#include<cstdio>
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 1000005;
int mq[maxn+5]; //单调队列,储存元素的索引
int f; //队头指针
int r; //队尾指针
int a[maxn+5];
int n;
int k;
void push_up(int i) //按升序插入
{
while(r > f && a[i] < a[mq[r-1]])//r-1指向的是队尾的元素,从队尾开始找位置插入
{
r--;
}
mq[r++] = i; //插入相应的位置
}
void push_down(int i) //按降序插入
{
while(r > f && a[i] > a[mq[r-1]])
{
r--;
}
mq[r++] = i; //插入相应的位置
}
int getFront()
{
return mq[f];
}
bool isempty()
{
return f == r; //队头指针和队尾指针同时指向一个地方说明队列为空
}
void pop() //删除队头,因为需要移动
{
f++;
}
void clearque()
{
f = r = 0;
}
void func(bool flag)
{
clearque(); //初始化没有元素插入的时候
if(flag == false) //打印minimum功能
{
int i;
for(i=1;i<=k && i<=n;i++) //k可能大于n
{
push_up(i);
}
printf("%d",a[getFront()]);
for(;i<=n;i++)
{
while(!isempty() && getFront()+ k <= i) //弹出不在滑动窗口内的元素
{
pop();
}
push_up(i);
printf(" %d",a[getFront()]);
}
printf("\n");
}
else //打印maximum的功能
{
int i;
for(i=1;i<=k && i<=n;i++)
{
push_down(i);
}
printf("%d",a[getFront()]);
for(;i<=n;i++)
{
while(!isempty() && getFront() + k <= i)
{
pop();
}
push_down(i);
printf(" %d",a[getFront()]);
}
printf("\n");
}
}
int main()
{
int i;
//freopen("111","r",stdin);
while(cin>>n>>k)
{
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
func(false); //minimum
func(true); //maximum
}
return 0;
}