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;
}