POJ-2823 Sliding Window

来源:互联网 发布:java第三方登录接口 编辑:程序博客网 时间:2024/06/17 17:40
Sliding Window
Time Limit: 12000MSMemory Limit: 65536KCase Time Limit: 5000MS

Description

An array of size n ≤ 106 is given to you. There is a sliding window of sizek which is moving from the very left of the array to the very right. You can only see thek 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 positionMinimum valueMaximum value[1  3  -1] -3  5  3  6  7 -13 1 [3  -1  -3] 5  3  6  7 -33 1  3 [-1  -3  5] 3  6  7 -35 1  3  -1 [-3  5  3] 6  7 -35 1  3  -1  -3 [5  3  6] 7 36 1  3  -1  -3  5 [3  6  7]37

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 integersn andk which are the lengths of the array and the sliding window. There aren 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 31 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 33 3 5 5 6 7

Source

POJ Monthly--2006.04.28, Ikki
——————————————————焦急的分割线——————————————————
思路:单调队列……单调队列……单调队列……咳咳,好吧,找最值的问题。首先你想到了二分查找。可是对于一个相当大的n和一个相当小的k,会调用多少次二分呢?妥妥地超时。据说线段树也可以解决此题,但是……我不会。而且线段树是比较耗时的。我们考虑这样一个问题,每一次在长度为k的区间内,找它里面的最大最小值,两个值都要找,暂时简化一下,只考虑找最大值。
怎么样扫一次就找到它呢?明显,每次区间向右移动一个数字而已。我们假设找到了第一个区间的最大值。那么每移动一次,只需要比较新的数字和最大值谁大。且慢,最大值如果在区间头部,就会丢弃。最大值丢了,新数字跟谁比较?无解了。但是牵扯到的数据结构绝对是队列。
解决方法是维护一个单调队列。寻找最大值的时候维护单调递减。那么队首一直是区间内的最大值。每次新数字进队,输出队首即可,一旦新数字与队首之间下标差值超过了区间长度,队首出队。一旦新数字比队尾大,出队直到第一个比它大的数。
这样一来,我们成功维护了一个区间长度“不变”(仅仅是涵义上不变)的队列。
代码如下:
#include <stdio.h>#include <string.h>#include <math.h>#include <stdlib.h>#define N 1000010typedef struct{    int val;    int id;}Que;//使用结构体保存队列中每个数字的下标和数值int num[N], ans[N], len, L, fron, rear, k, j;//num[]用来保存输入数据,ans[]、j、k仅仅用来输出,len是数组长度,L是区间长度Que a[N];//一个单调队列用两次,省一点儿内存void fun(int True){//True为1,维护寻找最小值的单调递增队列,反之维护寻找最大值的    k = fron = rear = 0;//初始化不要忘了    a[rear].val = num[0];    a[rear].id = 0;//第一个数先入队    for(int i = 1; i < len; i++){        if(i - a[fron].id >= L)  fron++;//一旦新数字与队首下标之差达到区间长度,踢了队首那个最值        if(True){            if(num[i] > a[rear].val){//保持了单调性,进队操作                a[++rear].val = num[i];                a[rear].id = i;            }            else{//打破单调性,出队操作                while(a[rear].val >= num[i] && fron <= rear)  rear--;                a[++rear].val = num[i];//完成出队之后,要用新数字覆盖最后一个出队的                a[rear].id = i;            }        }        else{//与True为1时同理,复制一下,更改一下大于号            if(num[i] < a[rear].val){                a[++rear].val = num[i];                a[rear].id = i;            }            else{                while(a[rear].val <= num[i] && fron <= rear)  rear--;                a[++rear].val = num[i];                a[rear].id = i;            }        }        if(i >= L-1)  ans[k++] = a[fron].val;//在本区间找到最值,保存到ans[]之中    }    for(j = 0; j < k-1; j++)//输出解        printf("%d ", ans[j]);    printf("%d\n", ans[j]);}int main(){while(~scanf("%d%d", &len, &L)){        for(int i = 0; i < len; i++)          scanf("%d", num+i);        if(L == 1){//这个比较坑人,区间长度为1的时候一定要按原样输出            int i;            for(i = 0; i < len-1; i++)                printf("%d ", num[i]);            printf("%d\n", num[i]);            for(i = 0; i < len-1; i++)                printf("%d ", num[i]);            printf("%d\n", num[i]);        }        else{            fun(1);//找最小值            fun(0);//找最大值        }    }return 0;}


0 0