思路
做法很多种,线段树,RMQ,单调队列。题意写的很明白,但是没有上述三种手段是无法暴力突破的,也算是单调队列的入门题了吧,单调队列的思想如下。
-
无论是单调递减队列还是单调递增队列头部一定是最小或者最大的。
-
给读入的每一个数字按顺序编号,由于每连续的K个中求最大和最小那么每次只维护前K个序列是单调队列的核心所在。一旦头部元素不在当前的连续K个范围内那么立即把头部的元素抛掉。
-
在维护前K个元素的时候, 以单调递减队列为例,如果当前元素的值比当前单调队列里最后一个元素要小,那么需要将队尾的元素抛出,必须严格维护该队列的单调性,否则这个队列将毫无意义。一直循环知道队列末尾的元素 小于 当前元素或者队列为空。
这道题北大也有POJ2823,但是deque会超时需要手动队列,G++也会超时,最后北大的我是用线段树过的复杂度O(nlogn)却可以过,单调队列的复杂度O(n)。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <deque>
#include <algorithm>
using namespace std;
const int maxn = 1e6+5;
struct info{
int n,pos;
};
deque<info>dmax,dmin;
int ans[maxn];
int main()
{
int n,k,x;
while(~scanf("%d%d",&n,&k)){
dmax.clear();dmin.clear();
int cnt = 0;
for(int i = 1;i <= n;i++){
scanf("%d",&x);
//头部元素超出范围需要抛掉
if(!dmax.empty() && i - dmax.front().pos >= k) dmax.pop_front();
if(!dmin.empty() && i - dmin.front().pos >= k) dmin.pop_front();
//尾部元素小于当前元素要抛掉,维护区间最大值,下面同理
while(!dmax.empty() && dmax.back().n < x) dmax.pop_back();
while(!dmin.empty() && dmin.back().n > x) dmin.pop_back();
info t;
t.n = x;t.pos = i;
dmax.push_back(t); //尾部插入
dmin.push_back(t); //尾部插入
if(i >= k){ //从第k个开始就需要输出一次,下标从1开始好理解。
if(i != n){
printf("%d ",dmin.front().n);
}
else{
printf("%d\n",dmin.front().n);
}
ans[cnt++] = dmax.front().n;
}
}
for(int i = 0;i < cnt-1;i++){
printf("%d ",ans[i]);
}
printf("%d\n",ans[cnt-1]);
}
return 0;
}
愿你走出半生,归来仍是少年~