题目链接:http://poj.org/problem?id=2823
题意:给出一个数列,长度为n,然后有一个宽度为k的窗口自数列最左端开始滑动至其右边到达数列的最右端,要求出整个过程中,滑动窗口在每个位置内的最大值和最小值。
同样是区间最值问题,线段树、RMQ、数状数组可以解决,不过此题有一点不同之处在于,其窗口是连续滑动的,貌似有动态规划的性质:
p[i][k]=max(p[i-1][k-1],a[i]);
复杂度很高,如果能快速求出p[i-1][k-1]就好了,这里有这样一种结构,单调队列:
以下摘自某博文:
一. 什么是单调(双端)队列
单调队列,顾名思义,就是一个元素单调的队列,那么就能保证队首的元素是最小(最大)的,从而满足动态规划的最优性问题的需求。
单调队列,又名双端队列。双端队列,就是说它不同于一般的队列只能在队首删除、队尾插入,它能够在队首、队尾同时进行删除。
【单调队列的性质】
一般,在动态规划的过程中,单调队列中每个元素一般存储的是两个值:
1. 在原数列中的位置(下标)
2. 他在动态规划中的状态值
而单调队列则保证这两个值同时单调。
从以上看,单调队列的元素最好用一个类来放,不这样的话,就要开两个数组。。。
插入方法(以最大单调队列为例):
在插入一个元素前,先判断队列是否为空(head<=tail),然后再判断队尾元素是否比要插入的元素小(q[tail].val<data[insert]),如果是的话,则将队尾元素删除(--tail)。
重复以上过程,直至为空或队尾元素比其大。
有了上述结构,就不需要求出p[i][k]( k = 1 ~ K);而是可能通过单调队列的单调性质,只需要删除队列中下标 j<i-k的元素,就可得到p[i];
Code:
#include<stdio.h> #include<string.h> #include<stdlib.h> #define M 1000008 typedef int Q; int n,k; int a[M],r[M]; Q q[M]; int front,tail; bool Cmpl(int a,int b) { return a>b; } bool Cmpr(int a,int b) { return a<b; } void Run(bool (*Cmp)(int ,int )) { int i,l; front=tail=0; for(i=l=0;i<n;i++){ while(front<tail&&(Cmp)(a[q[tail-1]],a[i]))//此处可以以二分来进行优化 tail--; q[tail++]=i; while(i-q[front]>=k) front++; r[l++]=a[q[front]]; } for(i=k-1;i<l;i++) printf(i-k+1?" %d":"%d",r[i]); puts(""); } int main() { int i; while(~scanf("%d%d",&n,&k)){ k=k>n?n:k; for(i=0;i<n;i++) scanf("%d",a+i); Run(Cmpl); Run(Cmpr); } return 0; }