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

题意:求每个长度为k的数组的最大值和最小值

思路:

1.用线段树创建维护最大值和最小值,遍历询问,简单复习了一下...有点手生

2.单调队列:

可以看一下详解

单调队列顾名思义就是一个单调递增或者递减的队列,我们可以通过队列瞬间得到当前队列的最大值和最小值。以查找当前区间最小值为例,我们需要维护一个递增的队列,那么当前队列队首就是最小值。在维护队列的过程中要注意:

1、如果队列的长度一定,要判断队首元素是否在规定范围内,如果超范围则队首移动,直到在范围内为止。

2、每次加入元素时和队尾比较,如果队尾元素大于要插入的元素且队列非空,则队尾元素依次出队,直到满足队列的单调性为止,这是为了保证队列的单调性。

线段树:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1e6+5;
int n,k,Max[N<<2],Min[N<<2],ansMa[N],ansMi[N],cnt;
void build(int l,int r,int rt){
    if(l == r){
        int a;
        scanf("%d",&a);
        Max[rt] = Min[rt] = a;
        return;
    }
    int m = (l + r) / 2;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    Max[rt] = max(Max[rt<<1],Max[rt<<1|1]);
    Min[rt] = min(Min[rt<<1],Min[rt<<1|1]);
}
int queryMa(int L,int R,int l,int r,int rt){
    if(l == r) return Max[rt];
    if(L <= l && R >= r){
        return Max[rt];
    }
    int m = (l + r) / 2;
    int MAX = -1e8;
    if(L <= m) MAX = max(MAX,queryMa(L,R,l,m,rt<<1));
    if(R > m) MAX = max(MAX,queryMa(L,R,m+1,r,rt<<1|1));
    return MAX;
}
int queryMi(int L,int R,int l,int r,int rt){
    if(l == r) return Min[rt];
    if(L <= l && R >= r){
        return Min[rt];
    }
    int m = (l + r) / 2;
    int MIN = 1e8;
    if(L <= m) MIN = min(MIN,queryMi(L,R,l,m,rt<<1));
    if(R > m) MIN = min(MIN,queryMi(L,R,m+1,r,rt<<1|1));
    return MIN;
}
int main(){
    while(~scanf("%d%d",&n,&k)){
        build(1,n,1);
        for(int i = 1,j = k;i <= n-k+1;i++,j++){
            ansMa[i] = queryMa(i,j,1,n,1);
            ansMi[i] = queryMi(i,j,1,n,1);
        }
        for(int i = 1;i <= n-k+1;i++){
            printf("%d ",ansMi[i]);
        }
        printf("
");
        for(int i = 1;i <= n-k+1;i++){
            printf("%d ",ansMa[i]);
        }
        printf("
");
    }
    return 0;
}

单调队列:

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 1e6+5;
int a[N],q[N],pos[N],n,k;
int MAX[N],MIN[N];
void get_max(){ //单调递减队列
    int head,tail;
    head = tail = 0;    //tail指向队尾(空)
    for(int i = 1;i < k;i++){
        while(head < tail && q[tail - 1] <= a[i])   //保证递减
            tail--;
        q[tail] = a[i];
        pos[tail++] = i;  //记录个数的位置,为后续查找q[head]是否超出范围准备
    }
    for(int i = k;i <= n;i++){
        while(head < tail && q[tail - 1] <= a[i])
            tail--;
        q[tail] = a[i];
        pos[tail++] = i;
        while(head < tail && pos[head] < i-k+1)
            head++;
        MAX[i-k] = q[head];
    }
    for(int i = 0;i < n-k+1;i++) printf("%d ",MAX[i]);
    printf("
");
}
void get_min(){ ////单调递增队列
    int head,tail;
    head = tail = 0;
    for(int i = 1;i < k;i++){
        while(head < tail && q[tail - 1] >= a[i])  //保证递增
            tail--;
        q[tail] = a[i];
        pos[tail++] = i;
    }
    for(int i = k;i <= n;i++){
        while(head < tail && q[tail - 1] >= a[i])
            tail--;
        q[tail] = a[i];
        pos[tail++] = i;
        while(head < tail && pos[head] < i-k+1)
            head++;
        MIN[i-k] = q[head];
    }
    for(int i = 0;i < n-k+1;i++) printf("%d ",MIN[i]);
    printf("
");
}
int main(){
    while(~scanf("%d%d",&n,&k)){
       for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
       get_min();
       get_max();
    }
    return 0;
}


原文地址:https://www.cnblogs.com/KirinSB/p/9408795.html