POJ2823 Sliding Window(单调队列)

Sliding Window
Time Limit: 12000MS   Memory Limit: 65536K
Total Submissions: 43271   Accepted: 12790
Case Time Limit: 5000MS

Description

An array of size  n ≤ 10 6 is given to you. There is a sliding window of size  k which is moving from the very left of the array to the very right. You can only see the  k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example: 
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

The input consists of two lines. The first line contains two integers  n and  k which are the lengths of the array and the sliding window. There are  n integers in the second line. 

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values. 

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
#include
#include
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;
}



你可能感兴趣的:(单调队列,poj(北大)OJ题目,单调队列)