首先我们考虑暴力做法。
O(n^2),枚举所有长度为k的区间,然后取区间最大值。
然后考虑一下优化,就是我们能不能枚举一遍就能求出答案,也就是取区间最大值能否在O(1)的时间内求出来呢?是可以的。我们需要一个队列来维护,为了每次直接O(1)求出最大值,我们需要构造一个单调递减的序列,这样每次取序列的第一个元素就是最大值。那么考虑一下当一个元素在和单调递减序列中间元素相同时,我们需要怎么做?为了使得这个序列是递减的我们需要将后面的元素全部丢弃,因为它对答案已经没有贡献,然后再把这个元素放到后面,使其继续保持递减性质。从上面的操作看我们需要一个可以在尾部进行插入与删除,在头部进行删除的容器(最大值不在区间内,需要将其移除),那么这个容器就是双端队列。
然后就出来了。递减只需要构造递增序列即可。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 20; typedef long long int ll; ll a[maxn]; void solved(){ ll n,k;cin>>n>>k; for(int i = 1; i <= n; i++)cin>>a[i]; deque<ll>m; for(int i = 1; i <= n; i++){ while(!m.empty() && i - k >= m.front())m.pop_front();// 考虑最大值是否越界(队头出队) while(!m.empty() && a[i] <= a[m.back()])m.pop_back(); //是否满足单调性(队尾出队) m.push_back(i); if(i >= k){ cout<<a[m.front()]<<" "; } } cout<<endl; deque<ll>M; for(int i = 1; i <= n; i++){ while(!M.empty() && i - k >= M.front())M.pop_front();// 考虑最大值是否越界(队头出队) while(!M.empty() && a[i] >= a[M.back()])M.pop_back(); //是否满足单调性(队尾出队) M.push_back(i); if(i >= k){ cout<<a[M.front()]<<" "; } } } int main(){ solved(); return 0; }