POJ 2823 Sliding Window (线段树/单调队列)
题目不说了,可以用线段树或者单调队列,下面附上代码。
线段树:
#include <iostream> #include <stdio.h> #include <algorithm> /* AC 线段树每个节点存储对应区间的最大值、最小值,然后直接查询即可。6594MS。 */ using namespace std; const int maxn=1000005; const int INF=0x3f3f3f3f; int minans[maxn],maxans[maxn]; int small,bigger; int n,k; struct Node{ int maxv,minv; }tree[maxn<<2]; void pushUp(int rt){ tree[rt].maxv=max(tree[rt<<1].maxv,tree[rt<<1|1].maxv); tree[rt].minv=min(tree[rt<<1].minv,tree[rt<<1|1].minv); } void build(int rt,int L,int R){ if(L==R){ scanf("%d",&tree[rt].minv); tree[rt].maxv=tree[rt].minv; return; } int mid=(L+R)>>1; build(rt<<1,L,mid); build(rt<<1|1,mid+1,R); pushUp(rt); } void query(int rt,int l,int r,int L,int R){ if(l<=L && R<=r){ small=min(small,tree[rt].minv); bigger=max(bigger,tree[rt].maxv); return; } int mid=(L+R)>>1; if(r<=mid) query(rt<<1,l,r,L,mid); else if(l>mid) query(rt<<1|1,l,r,mid+1,R); else{ query(rt<<1,l,r,L,mid); query(rt<<1|1,l,r,mid+1,R); } } int main() { int idx=0; scanf("%d%d",&n,&k); build(1,1,n); for(int i=1;i<=n-k+1;i++){ bigger=-INF; small=INF; query(1,i,i+k-1,1,n); minans[idx]=small; //存储最小值序列 maxans[idx]=bigger; //存储最大值序列 idx++; } for(int i=0;i<idx;i++){ if(i==0) printf("%d",minans[i]); else printf(" %d",minans[i]); } printf("\n"); for(int i=0;i<idx;i++){ if(i==0) printf("%d",maxans[i]); else printf(" %d",maxans[i]); } printf("\n"); return 0; }
单调队列:
#include <iostream> #include <stdio.h> /* AC 5204ms 利用单调队列,分两次处理,第一次求最小值序列,第二次求最大值序列。 以求最小值序列为例: 先处理前k-1个元素,每次从队尾加入队列之前,先与队尾元素比较,若比队尾元素值小,则删去队尾元素,直到有比它小的,或者队列为空, 将该元素插入到队尾。即每次保证队列为单调非减队列,队首元素始终是队列中最小的。 接下来依次读入一个元素,与队尾元素比较,若队尾元素大,则删去队尾元素,直至遇到比要插入的元素小或者队列为空为止,插入该元素。 由于要求的是连续的k个区间,所以还要把队列中在该区间之前的元素删去。 这里有个技巧,也就是队列中,存储的不是元素值,而是在数组中的位置,这样很容易判断某个元素是否在连续的k个区间内, 获取元素值的话,就通过存储的数组下标获取。 求最大值序列的时候,同最小值序列,保证队列为非增队列,即队列的首个元素一定是最大的。 */ using namespace std; const int maxn=1000005; int n,k; int a[maxn]; //n个元素值 int dequeue[maxn]; //单调队列 //求最小值序列 void minans(int k,int n) { int head=1; int tail=0; //先处理前k-1个元素 for(int i=0; i<k-1; i++) { while(head<=tail && a[dequeue[tail]]>=a[i]) { tail--; } tail++; dequeue[tail]=i; //存储的是元素在a中的索引 } for(int i=k-1; i<n; i++) { while(head<=tail && a[dequeue[tail]]>=a[i]) { tail--; } tail++; dequeue[tail]=i; while(dequeue[head]<i-k+1) //将索引在i-k+1之前的元素删去 head++; printf("%d ",a[dequeue[head]]); //输出队列首个元素,即最小的元素 } } //求最大值序列 void maxans(int k,int n) { int head=1; int tail=0; for(int i=0; i<k-1; i++) { while(head<=tail && a[dequeue[tail]]<=a[i]) { tail--; } tail++; dequeue[tail]=i; } for(int i=k-1; i<n; i++) { while(head<=tail && a[dequeue[tail]]<=a[i]) { tail--; } tail++; dequeue[tail]=i; while(dequeue[head]<i-k+1) head++; printf("%d ",a[dequeue[head]]); } } int main() { scanf("%d%d",&n,&k); for(int i=0; i<n; i++) { scanf("%d",&a[i]); } minans(k,n); printf("\n"); maxans(k,n); //printf("\n"); return 0; }