POJ-2823 Sliding Window

Sliding Window
Time Limit: 12000MSMemory Limit: 65536K
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 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 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

Source

POJ Monthly--2006.04.28, Ikki
——————————————————焦急的分割线——————————————————
思路:单调队列……单调队列……单调队列……咳咳,好吧,找最值的问题。首先你想到了二分查找。可是对于一个相当大的n和一个相当小的k,会调用多少次二分呢?妥妥地超时。据说线段树也可以解决此题,但是……我不会。而且线段树是比较耗时的。我们考虑这样一个问题,每一次在长度为k的区间内,找它里面的最大最小值,两个值都要找,暂时简化一下,只考虑找最大值。
怎么样扫一次就找到它呢?明显,每次区间向右移动一个数字而已。我们假设找到了第一个区间的最大值。那么每移动一次,只需要比较新的数字和最大值谁大。且慢,最大值如果在区间头部,就会丢弃。最大值丢了,新数字跟谁比较?无解了。但是牵扯到的数据结构绝对是队列。
解决方法是维护一个单调队列。寻找最大值的时候维护单调递减。那么队首一直是区间内的最大值。每次新数字进队,输出队首即可,一旦新数字与队首之间下标差值超过了区间长度,队首出队。一旦新数字比队尾大,出队直到第一个比它大的数。
这样一来,我们成功维护了一个区间长度“不变”(仅仅是涵义上不变)的队列。
代码如下:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define N 1000010
typedef 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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值