给定两个数n和k,接下来给出n个数的数列。每次维护一个长度为k的窗口,求出这个范围的最大值和最小值。每次向右移动一个单位。

考虑如何得出一个区间的最大值,每次增加一个数,如果这个数比当前的最大值小,如何是可以不用维护的。如果比当前值大,那么就要考虑。但是当这个最大值在最左边的时候,它下一次就不能在这个区间了。所以我们要维护的是一个值可以存在的最左的位置。即位置i的生存期最多是i+k,但如果中间出现比它大的数,那么就没必要维护这个数了,可以用出现的数代替它。因为它总是比维护当前的数更优,无论是数值上还是位置上。

也就是说,我们要维护一个递减的数列,可以用单调队列实现做到O(n)的复杂度。

最小值同理,代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;

int n,k,v[2333333],q[23333333];
int main() {
    while(~scanf("%d%d",&n,&k)){
        for(int i=1;i<=n;++i)
            scanf("%d",&v[i]);
        
        int l=1,r=1;
        q[1]=0;
        v[0]=1e9;
        for(int i=1;i<=n+1;++i){
            while(l<=r&&q[l]+k<i)
                ++l;
            if(i<=k)
                ;
            else printf("%d ",v[q[l]]);
            while(l<=r&&v[q[r]]>=v[i])
                --r;
            q[++r]=i;
        }
        puts("");
        //printf("%d\n",v[q[l]]);
        l=1,r=1;
        q[1]=0;
        v[0]=-1e9;
        for(int i=1;i<=n+1;++i){
            while(l<=r&&q[l]+k<i)
                ++l;
            /*
            for(int j=l;j<=r;++j)
            printf("%d ",v[q[j]]);
            puts("");
            */
            if(i<=k)
                ;
            else 
                printf("%d ",v[q[l]]);
            while(l<=r&&v[q[r]]<=v[i])
                --r;
            q[++r]=i;
        }
        puts("");
        //printf("%d\n",v[q[l]]);
    }
    return 0;
}

 

posted on 2018-04-28 19:46  chagin  阅读(101)  评论(0编辑  收藏  举报