现在的位置: 首页 > 综合 > 正文

单调队列

2013年01月19日 ⁄ 综合 ⁄ 共 2101字 ⁄ 字号 评论关闭

POJ  2823:

也就是有一个数列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)。

文字内容转自:http://www.sunhongfeng.com/2011/07/%E5%8D%95%E8%B0%83%E9%98%9F%E5%88%97-poj2823/

#include<iostream>
#include<string.h>
#define size 1000010
using namespace std;
int a[size];
int ans1[size],ans2[size];
int s1,t1,s2,t2;
typedef struct
{
     int x,t;
}Q;
Q up[size],down[size]; 
void insertmin(int i)
{
     while(up[t1].x>a[i]&&s1<=t1) t1--;
     up[++t1].x=a[i];
     up[t1].t=i;
   //  for(int j=s1;j<=t1;j++) cout<<"up= "<<up[j].x<<" "; cout<<endl;
}
int getmin(int i)
{
    while(up[s1].t<i) s1++;
  //  cout<<"   min "<<up[s1].t<<" "<<up[s1].x<<endl;
    return up[s1].x;
}
void insertmax(int i)
{
     while(down[t2].x<a[i]&&s2<=t2) t2--;
     down[++t2].x=a[i];
     down[t2].t=i;
    // for(int j=s2;j<=t2;j++) cout<<"down= "<<down[j].x<<" ";cout<<endl;
}
int getmax(int i)
{
    while(down[s2].t<i) s2++;
   // cout<<"   max "<<down[s2].t<<" "<<down[s2].x<<endl;
    return down[s2].x;
}
int main()
{
    int n,k;
    int p1,p2;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
         for(int i=0;i<n;i++) scanf("%d",&a[i]);
         p1=p2=-1;
         up[0].x=down[0].x=a[0];
         up[0].t=down[0].t=0;
         s1=s2=t1=t2=0;
         for(int i=1;i<k;i++) {insertmin(i);insertmax(i);}
         ans1[++p1]=getmin(0);
         ans2[++p2]=getmax(0);
         for(int i=k;i<n;i++)
         {
              insertmin(i);
              insertmax(i);
              ans1[++p1]=getmin(i-k+1);
              ans2[++p2]=getmax(i-k+1);
         }
         for(int i=0;i<p1;i++) printf("%d ",ans1[i]); printf("%d\n",ans1[p1]);
         for(int i=0;i<p2;i++) printf("%d ",ans2[i]); printf("%d\n",ans2[p2]);
    } 
    return 0;
} 

抱歉!评论已关闭.