POJ 2823 Sliding Window (单调队列 | 线段树)

Sliding Window
Time Limit: 12000MS   Memory Limit: 65536K
Total Submissions: 29829   Accepted: 8863
Case Time Limit: 5000MS

Description

An array of size n ≤ 106 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  -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

Source

  

单调递减队列是这么一个队列,它的头元素一直是队列当中的最大值,而且队列中的值是按照递减的顺序排列的。我们可以从队列的末尾插入一个元素,可以从队列的两端删除元素。

1.首先看插入元素:为了保证队列的递减性,我们在插入元素v的时候,要将队尾的元素和v比较,如果队尾的元素不大于v,则删除队尾的元素,然后继续将新的队尾的元素与v比较,直到队尾的元素大于v,这个时候我们才将v插入到队尾。

2.队尾的删除刚刚已经说了,那么队首的元素什么时候删除呢?由于我们只需要保存i的前k-1个元素中的最大值,所以当队首的元素的索引或下标小于i-k+1的时候,就说明队首的元素对于求f(i)已经没有意义了,因为它已经不在窗里面了。所以当index[队首元素]<i-k+1时,将队首元素删除。< p="">

从上面的介绍当中,我们知道,单调队列与队列唯一的不同就在于它不仅要保存元素的值,而且要保存元素的索引(当然在实际应用中我们可以只需要保存索引,而通过索引间接找到当前索引的值)。

为了让读者更明白一点,我举个简单的例子。

假设数列为:8,7,12,5,16,9,17,2,4,6.N=10,k=3.

那么我们构造一个长度为3的单调递减队列:

首先,那8和它的索引0放入队列中,我们用(8,0)表示,每一步插入元素时队列中的元素如下:

0:插入8,队列为:(8,0)

1:插入7,队列为:(8,0),(7,1)

2:插入12,队列为:(12,2)

3:插入5,队列为:(12,2),(5,3)

4:插入16,队列为:(16,4)

5:插入9,队列为:(16,4),(9,5)

。。。。依此类推

那么f(i)就是第i步时队列当中的首元素:8,8,12,12,16,16,。。。

/*
单调队列
适合解决的问题,多次查询k个连续序列中最大或最小值。可以将复杂度从O(n*n)缩短到O(n)。
实现模式:队列实现,只不过其中元素单调(依次增大或减小),我们假设求最大值。入队时比较队尾元素与插入元素的大小,如果队尾元素小与插入元素,则对尾元素出对,直到大于等于位置。
这样插入k个元素后,队列中队首即为最大值。继续进行第二次查询时,需要比较队首元素在原来元素中的位置,判断是否在这次查询的区间内,如果不在,则出对。使用自定义数组即可实现。
*/

注意用C++提交,否则TLE

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int N=1000010;

int n,k,num[N];

struct Que{
    int loc,x;
    Que(){}
    Que(int _loc,int _x):loc(_loc),x(_x){}
}minQ[N],maxQ[N];   //一个最小队列,一个最大队列

int minl,minr,maxl,maxr;

int main(){

    //freopen("input.txt","r",stdin);

    while(~scanf("%d%d",&n,&k)){
        for(int i=0;i<n;i++)
            scanf("%d",&num[i]);

        minl=minr=0;
        int i;
        for(i=0;i<k;i++){   //队列初始化
            while(minr>0 && minQ[minr-1].x>num[i])
                minr--;
            minQ[minr++]=Que(i,num[i]);
        }
        printf("%d",minQ[minl].x);
        for(;i<n;i++){
            if(minQ[minl].loc<i-k+1)    //判断队首元素是否还在查询范围之内
                minl++;
            while(minr>minl && minQ[minr-1].x>num[i])   //循环比较队尾元素与插入元素的大小,直到对尾元素<=插入元素或者队为空
                minr--;
            minQ[minr++]=Que(i,num[i]);     //入队
            printf(" %d",minQ[minl].x);
        }
        printf("\n");

        maxl=maxr=0;    //与上面求最小值相同
        for(i=0;i<k;i++){
            while(maxr>0 && maxQ[maxr-1].x<num[i])
                maxr--;
            maxQ[maxr++]=Que(i,num[i]);
        }
        printf("%d",maxQ[maxl].x);
        for(;i<n;i++){
            if(maxQ[maxl].loc<i-k+1)
                maxl++;
            while(maxr>maxl && maxQ[maxr-1].x<num[i])
                maxr--;
            maxQ[maxr++]=Que(i,num[i]);
            printf(" %d",maxQ[maxl].x);
        }
        printf("\n");
    }
    return 0;
}
注意用C++提交,否则TLE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1000010;
const int INF=0x3f3f3f3f;

#define L(rt) (rt<<1)
#define R(rt) (rt<<1|1)

struct Tree{
    int l,r;
    int minx,maxx;
}tree[N<<2];

void build(int L,int R,int rt){
    tree[rt].l=L;
    tree[rt].r=R;
    if(tree[rt].l==tree[rt].r){
        scanf("%d",&tree[rt].minx);
        tree[rt].maxx=tree[rt].minx;
        return ;
    }
    int mid=(L+R)>>1;
    build(L,mid,L(rt));
    build(mid+1,R,R(rt));
    tree[rt].minx=min(tree[L(rt)].minx,tree[R(rt)].minx);
    tree[rt].maxx=max(tree[L(rt)].maxx,tree[R(rt)].maxx);
}

int minx,maxx;

void QueMin(int L,int R,int rt){
    if(L<=tree[rt].l && tree[rt].r<=R){
        minx=min(minx,tree[rt].minx);
        return ;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(R<=mid)
        QueMin(L,R,L(rt));
    else if(L>=mid+1)
        QueMin(L,R,R(rt));
    else{
        QueMin(L,mid,L(rt));
        QueMin(mid+1,R,R(rt));
    }
}

void QueMax(int L,int R,int rt){
    if(L<=tree[rt].l && tree[rt].r<=R){
        maxx=max(maxx,tree[rt].maxx);
        return ;
    }
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(R<=mid)
        QueMax(L,R,L(rt));
    else if(L>=mid+1)
        QueMax(L,R,R(rt));
    else{
        QueMax(L,mid,L(rt));
        QueMax(mid+1,R,R(rt));
    }
}

int main(){

    //freopen("input.txt","r",stdin);

    int n,k;
    while(~scanf("%d%d",&n,&k)){
        build(1,n,1);
        int m=n-k,r;
        for(int l=1;l<=m;l++){
            r=l+k-1;
            minx=INF;
            QueMin(l,r,1);
            printf("%d ",minx);
        }
        minx=INF;
        QueMin(n-k+1,n,1);
        printf("%d\n",minx);

        for(int l=1;l<=m;l++){
            r=l+k-1;
            maxx=-INF;
            QueMax(l,r,1);
            printf("%d ",maxx);
        }
        maxx=-INF;
        QueMax(n-k+1,n,1);
        printf("%d\n",maxx);
    }
    return 0;
}
 
原文地址:https://www.cnblogs.com/jackge/p/3022564.html