POJ 2823 (从经典滑动窗口最大值问题入门单调队列)

题目大意
输入一个长度为n(n≤≤106106)的数列,给定一个长度为k的窗口,让这个窗口在数列上移动,求移动到每个位置窗口中包含数的最大值和最小值。即设序列为A1A1,A2A2,…,AnAn,设f(i)=min{Ai−k+1Ai−k+1,Ai−k+2Ai−k+2,…,AkAk} ,g(i)=max{Ai−k+1Ai−k+1,Ai−k+2Ai−k+2,…,AkAk}
求:f(k),f(k+1),…,f(n) g(k),g(k+1),…,g(n).

分析
本题算是单调队列的经典入门题。因此拿来学习单调队列。
如果单纯对每一个i都去扫描前面k个数去求最值,那复杂度为o(n*k),肯定超时。
如果用线段树或者树状数组维护区间最值,那复杂度为o(nlogn)。
但还有更简洁更高效的办法,即单调队列。
单调队列是一种特殊的队列,它能在队列两端进行删除操作,并始终维护队列保持一种单调性。
以求窗口最小值为例,我们需要维护一个内部元素值递减的队列来模拟滑动窗口,使每一次查询取队首元素即为答案。如何维护呢?有以下两种操作:

1.从队尾插入元素:当有新元素需要入队时,让它与当前队尾元素进行比较,若它小于等于当前队尾元素(即破坏了原队列的单调性),那么删除队尾元素,并继续比较队尾与新元素,直到找到一个队尾大于新元素时,将新元素插入到队尾。被删除的元素既比新元素大,又会比新元素先滑出窗口,因此肯定不会成为答案。这个操作不断维护了队列中的最值。

2.删除队首元素:由于序列中的元素当且仅当在滑动窗口时有效,因此,当队首元素不在滑动窗口内时,就删除队首元素。这个操作维护了数据的临时性。

元素是否过时与其下标有关,因此我队列中存储的是序列数对应的下标,这样每当要入队一个数,只需访问队首元素所存下标在序列中对应的数即可得到答案。
这样做尽管插入元素时有时需删除多个数,但每个数最多被删除一次,因此时间复杂度为O(n).
AC code

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000000+5;
typedef struct Node{
  int num;
  int i;
}Node;

Node dq[2*maxn];
int n,m;
int num[maxn];

inline void get_max(int n,int m){//单调递减队列
    for(int i=0;i<=n;i++)
      dq[i].num=dq[i].i=0;
    dq[0].num=0x3f3f3f3f;
    int head=1,tail=1;
    for(int i=1;i<=m;i++){
      while(head<=tail && dq[tail].num<=num[i])
        tail--;
      tail++;
      dq[tail].num=num[i];
      dq[tail].i=i;
    }
    for(int i=m;i<=n;i++){
      while(head<=tail && dq[tail].num<=num[i])
        tail--;
      tail++;
      dq[tail].num=num[i];
      dq[tail].i=i;
      while(dq[head].i<=i-m)
        head++;
      cout<<dq[head].num<<' ';
    }
    cout<<endl;
}


inline void get_min(int n,int m){//单点递增队列
    for(int i=0;i<=n;i++)
      dq[i].num=dq[i].i=0;
    dq[0].num=-0x3f3f3f3f;
    int head=1,tail=1;
    for(int i=1;i<=m;i++){
      while(head<=tail && dq[tail].num>=num[i])
        tail--;
      tail++;
      dq[tail].num=num[i];
      dq[tail].i=i;
    }
    for(int i=m;i<=n;i++){
      while(head<=tail && dq[tail].num>=num[i])
        tail--;
      tail++;
      dq[tail].num=num[i];
      dq[tail].i=i;
      while(dq[head].i<=i-m)
        head++;
      cout<<dq[head].num<<' ';
    }
    cout<<endl;
}

int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++){
    cin>>num[i];
  }

  get_min(n,m);
  get_max(n,m);

  return 0;
}
发布了54 篇原创文章 · 获赞 28 · 访问量 7310

猜你喜欢

转载自blog.csdn.net/jiangkun0331/article/details/96910945
今日推荐