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