\(luogu\ P1886\)滑动窗口
这道题目比较简单,但是因为经常忘记单调队列做滑动窗口所以写博客来加深一下印象。
如果求区间最小值,我们用发现右端点从前往后扫的方法一个数如果有贡献,当且仅当当前扫描的右端点的前面到这个数中间没有比这个数更小的数,因为如果有比这个数更小的数的话,这个更小的数肯定就会成为区间的最小值。如果一个数没有贡献的时候就是区间的左端点比这个数的下标要大的时候。
所以我们用一个双端队列来维护,每次进入队列的时候检查队尾的数如果比要加入得数大的话,就不断弹出队尾,之后我们将这个数压入队尾,然后我们在检查一下队列头的数的下标是否比枚举到的区间左端点大,如果小的话就弹出队头。这样之后答案就是队头的数。
重点是标程。
#include<bits/stdc++.h>
#include<vector>
using std::vector;
const int N=1e6+100;
vector<int>v;
int n,k;
int a[N];
inline int read()
{
int ans=0,p=1;
char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') p=-1;ch=getchar();}
while (ch>='0'&&ch<='9') {ans=ans*10-'0'+ch;ch=getchar();}
return ans*p;
}
int main()
{
n=read();k=read();
for (int i=1;i<=n;i++)
a[i]=read();
for (int i=1;i<=n;i++)
{
while (!v.empty()&&a[v.back()]>a[i]) v.pop_back();
v.push_back(i);
if (!v.empty()&&v.front()<i-k+1) v.erase(v.begin(),v.begin()+1);
if (i>=k) printf("%d ",a[v.front()]);
}
printf("\n");
v.clear();
for (int i=1;i<=n;i++)
{
while (!v.empty()&&a[v.back()]<a[i]) v.pop_back();
v.push_back(i);
if (!v.empty()&&v.front()<i-k+1) v.erase(v.begin(),v.begin()+1);
if (i>=k) printf("%d ",a[v.front()]);
}
return 0;
}
如果要开多个队列,推荐使用 \(vector\) ,不信可以开 \(10^5\) 个 \(deque\) 和 \(vector\) 试一下。
原文地址:https://www.cnblogs.com/last-diary/p/11562080.html
时间: 2024-11-07 17:57:16