POJ 2823 Sliding Window

1.题目描述:点击打开链接

2.解题思路:本题利用单调队列解决。单调队列和单调栈性质一样,内部元素严格单调递增排列。单调队列的一个典型应用就是本题的求滑动窗口的最值问题。那么怎么求解呢?首先,由于长度为k,因此我们可以先把0到k-1的下标全部试图入队列。在加入元素i时,若队列的末尾的值j满足Aj≥Ai,则不断地取出,直到队列为空或者Aj<Ai之后再在末尾加入i。此时,如果k-1也加入到了队列时,查看队列头部的值j,那么B0=Aj。如果队列头部元素==i-k+1,那么这个元素以后肯定用不到了,删去。这样就求出了最小值。用同样类似的写法可以求出最大值。

还可以简单的理解:从头滑到尾可以求出最小值,从尾滑到头可以求出最大值。

3.代码:

//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
using namespace std;

#define me(s)  memset(s,0,sizeof(s))
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;

const int N=1000000+10;
int n,k;
int a[N];
int b[N];
int c[N];
int deq[N];

int main()
{
    while(~scanf("%d%d",&n,&k)&&(n||k))
    {
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        int s=0,t=0;         //求最小值
        for(int i=0;i<n;i++)
        {
            while(s<t&&a[deq[t-1]]>=a[i])t--; //不满足严格单调递增性,不断地取出
            deq[t++]=i;   //从尾部入队列
            if(i-k+1>=0)b[i-k+1]=a[deq[s]]; //如果滑动窗口起点变为非负数,那么最小值就是a[deq[s]]
            if(deq[s]==i-k+1)s++;  //说明滑动窗口的起点和队首相同,以后的滑动窗口肯定用不到队首元素了,删去
        }
        s=t=n;             //用同样的方法求最大值,只不过要逆序
        for(int i=n-1;i>=0;i--)
        {
            while(s<t&&a[i]>=a[deq[s]])s++;
            deq[--s]=i;  //从头部插入元素
            if(i+k-1<n)c[i]=a[deq[t-1]];   //如果滑动窗口的终点<n,那么最大值等于a[deq[t-1]]
            if(deq[t-1]==i+k-1)t--;   //如果滑动窗口的终点和队尾相同,以后的滑动窗口肯定用不到队尾了,删去
        }
        for(int i=0;i<=n-k;i++)
            printf("%d%c",b[i]," \n"[i==n-k]);
        for(int i=0;i<=n-k;i++)
            printf("%d%c",c[i]," \n"[i==n-k]);
    }
}

你可能感兴趣的:(单调队列)