一、概念介绍

1、 双端队列

双端队列是一种线性表,是一种特殊的队列,遵守先进先出的原则。双端队列支持以下4种操作:

  • (1)   从队首删除
  • (2)   从队尾删除
  • (3)   从队尾插入
  • (4)   查询线性表中任意一元素的值

2、 单调队列

单调队列是一种特殊的双端队列,其内部元素具有单调性。最大队列与最小队列是两种比较常用的单调队列,其内部元素分别是严格单调递减(不是非递增)和严格单调递增(不是非递减)的。

单调队列的常用操作如下:

  • (1) 插入:若新元素从队尾插入后会破坏单调性,则删除队尾元素,直到插入后不再破坏单调性为止,再将其插入单调队列。
  • (2) 获取最优(最大、最小)值:访问队首元素

以下是一个单调递增队列的例子:

队列大小不能超过3,入队元素依次为3,2,8,4,5,7,6,4

3入队:(3)

3从队尾出队,2入队:(2)

8入队:(2,8)

8从队尾出队,4入队:(2,4)

5入队:(2,4,5)

2从队头出队,7入队:(4,5,7)

7从队尾出队,6入队:(4,5,6)

6从队尾出队,5从队尾出队,4从队尾出队,4入队:(4)

以上左端为队头,右端为队尾。

二、单调队列的应用

1、最大值的维护:

       比如我们要维护一个区间为k的最大值的单调队列,由于新插入 的节点他的“生命力”肯定比原先已经在队列中的元素“活”的时间长,将插入元素不断与队尾元素比, 如果他大于队尾元素,那么tail--将队尾元素删掉,(因为目前插入的这个元素值(设为pos)更大,而且“活”的时间 长,有pos在,队尾元素的有“生”之年永远都没法为最大值,故而直接无视比pos小的队尾了)。直到对空位置或者 找到了一个比pos大的队尾。

2、poj 2823 Sliding Window

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 position Minimum value Maximum 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

思路

 一、

  这道题目有个非常重要的信息,即所有的区间都是等长且连续的,那么对于“相邻”两个区间(l,r)与(l+1,r+1)有些极优美的性质:al,al+1,al+2,…,ar-1,ar,ar+1,以最大值为例:我们注意到,在区间(l,r)中,Max(al,al+1,al+2,…,ar-1,ar)=max(al,max(al+1,al+2,…,ar-1,ar))

在区间(l+1,r+1)中,Max(al+1,al+2,…,ar-1,ar,ar+1)=max(max(al+1,al+2,…,ar-1,ar),ar+1)

两个式子中有相同的部分max(al+1,al+2,…,ar-1,ar),经验告诉我们,区间(l,r)中最大值落在(l+1,r)区间的概率很大。那么,在求(l+1,r+1)的最值时,我们完全没有必要再扫描一次。只有当上一次的最大值落在了al上时才需要重新扫描,这样,算法得到了极大的优化。

  继续考虑这样一个问题,以最大值为例,对于任意l<=i<=j<=r,如果ai<aj,那么,在区间向右移动的过程中,最大值永远也不会落在ai上,因为ai比aj先失效,能用ai一定能用aj,此时我们便不再需要ai了。这个性质与单调队列性质重合了。在扫描这个数列的时候,我们维护一个单调递减的队列。当我们将区间从(l,r)移动到(l+1,r+1)时,将ar+1 插入单调队列,若队首元素不在(l+1,r+1)时,删除它。这样处理后的队首元素便是(l+1,r+1)区间中的最大值。

二、

看这个问题: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.Your task is to determine the maximum and minimum values in the sliding window at each position.

也就是有一个数列a,要求你求数列b和c,b[i]是a[i]…a[i+w-1]中的最小值,c[i]是最大值。如果a是1,3,-1,-3,5,3,6,7,则b为-1,-3,-3,-3,3,3,c为3,3,5,5,6,7。

  这个问题相当于一个数据流(数列a)在不断地到来,而数据是不断过期的,相当于我们只能保存有限的数据(sliding window中的数据,此题中就是窗口的宽度w),对于到来的查询(此题中查询是每时刻都有的),我们要返回当前滑动窗口中的最大值最小值。注意,元素是不断过期的。

解决这个问题可以使用一种叫做单调队列的数据结构,它维护这样一种队列:

  • a)从队头到队尾,元素在我们所关注的指标下是递减的(严格递减,而不是非递增),比如查询如果每次问的是窗口内的最小值,那么队列中元素从左至右就应该递增,如果每次问的是窗口内的最大值,则应该递减,依此类推。这是为了保证每次查询只需要取队头元素。
  • b)从队头到队尾,元素对应的时刻(此题中是该元素在数列a中的下标)是递增的,但不要求连续,这是为了保证最左面的元素总是最先过期,且每当有新元素来临的时候一定是插入队尾。

满足以上两点的队列就是单调队列,首先,只有第一个元素的序列一定是单调队列。

那么怎么维护这个单调队列呢?无非是处理插入和查询两个操作。

  对于插入,由于性质b,因此来的新元素插入到队列的最后就能维持b)继续成立。但是为了维护a)的成立,即元素在我们关注的指标下递减,从队尾插入新元素的时候可能要删除队尾的一些元素,具体说来就是,找到第一个大于(在所关注指标下)新元素的元素,删除其后所有元素,并将新元素插于其后。因为所有被删除的元素都比新元素要小,而且比新元素要旧,因此在以后的任何查询中都不可能成为答案,所以可以放心删除。

对于查询,由于性质b,因此所有该时刻过期的元素一定都集中在队头,因此利用查询的时机删除队头所有过期的元素,在不含过期元素后,队头得元素就是查询的答案(性质a),将其返回即可。由于每个元素都进队出队一次,因此摊销复杂度为O(n)。

#include<stdio.h>
#include<string.h>
const int maxn = 1000005;
int a[maxn],q[maxn];
int n,k;

void Monotonequeue(int op)
{
    memset(q,0,sizeof(q));
    bool first = true;
    int head = 1,tail = 0;
    for (int i = 1; i <= n; i++)
    {
        while (head <= tail && ((!op && a[i] <= a[q[tail]]) || (op && a[i] >= a[q[tail]])))	tail--;
        q[++tail] = i;
        while (head <= tail && q[tail] - q[head] >= k)	head++;
        if (i >= k)	first?printf("%d",a[q[head]]):printf(" %d",a[q[head]]),first = false;
    }
    printf("\n");
}

int main()
{
    scanf("%d%d",&n,&k);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&a[i]);
    }
    Monotonequeue(0); //维护单调递增队列,队头元素最小
    Monotonequeue(1); //维护单调递减队列,队头元素最大
    return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 LL;
const int maxn = 1000005;
struct node
{
    int pos,val;
} q[maxn];

int num[maxn],n,k;
int Min[maxn];
int Max[maxn];

void getMin()
{
    int head=1,tail=0;
    for(int i=1; i<k; i++)
    {
        //前k-1个数跟查询无关,直接放进来
        while(head<=tail&&num[i]<q[tail].val) tail--;
        //插入的准备操作,要么队列为空,要么找到一个位置使得符合单调队列定义
        tail++;
        q[tail].val=num[i];
        q[tail].pos=i;
        //插入
    }
    for(int i=k; i<=n; i++)
    {
        while(head<=tail&&num[i]<q[tail].val) tail--;
        tail++;
        q[tail].val=num[i];
        q[tail].pos=i;
        while(i-q[head].pos>=k) head++;
        //删除操作,维护最大的区间长度
        Min[i-k]=q[head].val;
        //答案记录
    }
}

void getMax()
{
    int head=1,tail=0;
    for(int i=1; i<k; i++)
    {
        while(head<=tail&&num[i]>q[tail].val) tail--;
        tail++;
        q[tail].val=num[i];
        q[tail].pos=i;
    }
    for(int i=k; i<=n; i++)
    {
        while(head<=tail&&num[i]>q[tail].val) tail--;
        tail++;
        q[tail].val=num[i];
        q[tail].pos=i;
        while(i-q[head].pos>=k) head++;
        Max[i-k]=q[head].val;
    }
}

int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        for(int i=1; i<=n; i++) scanf("%d",&num[i]);
        getMin();
        for(int i=0; i<=n-k; i++) printf("%d%c",Min[i],i==n-k?'\n':' ');
        getMax();
        for(int i=0; i<=n-k; i++) printf("%d%c",Max[i],i==n-k?'\n':' ');
    }
    return 0;
}

 

POJ 2823 Sliding Window + 单调队列的更多相关文章

  1. poj 2823 Sliding Window (单调队列入门)

    /***************************************************************** 题目: Sliding Window(poj 2823) 链接: ...

  2. POJ 2823 Sliding Window (单调队列)

    单调队列 加了读入挂比不加更慢.... 而且这份代码要交c++ 有大神G++跑了700ms..... orzorzorz #include<iostream> #include<cs ...

  3. poj 2823 Sliding Windows (单调队列+输入输出挂)

    Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 73426   Accepted: 20849 ...

  4. POJ 2823 Sliding Window 题解

    POJ 2823 Sliding  Window 题解 Description An array of size n ≤ 106 is given to you. There is a sliding ...

  5. 洛谷P1886 滑动窗口(POJ.2823 Sliding Window)(区间最值)

    To 洛谷.1886 滑动窗口 To POJ.2823 Sliding Window 题目描述 现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口.现在这个从左边开始向右滑动,每 ...

  6. POJ 2823 Sliding Window(单调队列入门题)

      Sliding Window Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 67218   Accepted: 190 ...

  7. POJ 2823 Sliding Window 【单调队列】

    题目链接:http://poj.org/problem?id=2823 题目大意:给出一组数,一个固定大小的窗体在这个数组上滑动,要求出每次滑动该窗体内的最大值和最小值. 这就是典型的单调队列,单调队 ...

  8. 【单调队列】poj 2823 Sliding Window

    http://poj.org/problem?id=2823 [题意] 给定一个长度为n的序列,求长度为k的滑窗内的最大值和最小值 [思路] 裸的单调队列 注意用C++提交,不然会T,orz我用G++ ...

  9. 题解报告:poj 2823 Sliding Window(单调队列)

    Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is m ...

随机推荐

  1. sql基本命令

    --------------------------------------------------------SQL基本命令开始----------------------------------- ...

  2. 网站集成QQ登录功能

    最近在做一个项目时,客户要求网站能够集成QQ登录的功能,以前没做过这方面的开发,于是去QQ的开放平台官网研究了一下相关资料,经过自己的艰苦探索,终于实现了集成QQ登录的功能,现在把相关的开发经验总结一 ...

  3. 20151023 - discuz 6 中 insenz 营销推广失效的问题

    将很久之前的论坛重新放在网络上,发现首页打开非常慢,用 Web Inspector 检查,发现 insenz.com 已失效导致. 解决办法: 1.进入数据库:执行 SELECT * FROM cdb ...

  4. [HDOJ5442]Favorite Donut(最大表示法)

    嗯……就是最小表示法改一下…… 这题就是把S串当作两个判断同构的串,然后就搞出最大的表示了 然后在反向再做一次 O(n)求最大表示,O(n)判断正反谁大

  5. jsp还是html+ajax

    1.有人说JSP会泄露源码(可能会有一些代码痕迹,但肯定没啥大事)2.又说,Ajax是为了分离前后台,让控制部分在前台处理,降低代码耦合度,后台只相当于服务. 3.能够让前台移植,降低后期维护成本.纯 ...

  6. android 按钮点击效果实现

    在其他人的博客里看到其实实现按钮点击效果的方法有很多,这里提到的只是其中一个办法 图片素材(我自己用截图截的,可以自己搞) 放到mipmap目录下(studio是在这个目录下 , eclipse 直接 ...

  7. QMenu,contextmenuevent,窗体透明

    void MainWindow::contextMenuEvent(QContextMenuEvent *event) { QMenu *menu=newQMenu; menu->addActi ...

  8. git 查看生成对象

    1. find .   查看目录中所有对象 2. find .git/objects 查看所有对象 3. git cat-file -p 散列值  输出文件内容

  9. Tomcat-在发布项目时两次重复加载的问题介绍与解决

      前提: 如 1.存在某个应用:hello 2.该应用存放路径:D:\apache-tomcat-5.5.17\webapps\hello 3.Tomcat的server.xml部分配置信息如下: ...

  10. 第6届蓝桥杯javaA组第7题,牌型种数,一道简单的题带来的思考

    题目: 小明被劫持到X赌城,被迫与其他3人玩牌. 一副扑克牌(去掉大小王牌,共52张),均匀发给4个人,每个人13张. 这时,小明脑子里突然冒出一个问题: 如果不考虑花色,只考虑点数,也不考虑自己得到 ...