洛谷 滑动窗口 /【模板】单调队列
题目描述
有一个长为 n 的序列 a,以及一个大小为 k 的窗口。现在这个窗口从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最小值和最大值。
例如,对于序列 [1,3,−1,−3,5,3,6,7] 以及 k=3,有如下过程:
窗口位置[1 3 -1] -3 5 3 6 7 1 [3 -1 -3] 5 3 6 7 1 3 [-1 -3 5] 3 6 7 1 3 -1 [-3 5 3] 6 7 1 3 -1 -3 [5 3 6] 7 1 3 -1 -3 5 [3 6 7]最小值−1−3−3−333最大值335567
输入格式
输入一共有两行,第一行有两个正整数 n,k;
第二行有 n 个整数,表示序列 a。
输出格式
输出共两行,第一行为每次窗口滑动的最小值;
第二行为每次窗口滑动的最大值。
输入输出样例
输入 #1复制
8 3 1 3 -1 -3 5 3 6 7
输出 #1复制
-1 -3 -3 -3 3 3 3 3 5 5 6 7
说明/提示
【数据范围】
对于 50% 的数据,1≤n≤105;
对于 100% 的数据,1≤k≤n≤106,ai∈[−231,231)。
代码1:超时??因为当n = 10^6,k = 10^6,循环10^12次,超时。
#include <bits/stdc++.h>
#define MX 1000005
using namespace std;
int a[MX],b[MX],c[MX];
int main() {
int n,k;
cin>>n>>k;
for(int i = 1; i <= n; i++) {
cin>>a[i];
}
for(int left = 1; left+k-1<= n; left++) {
int mn = INT_MAX,mx = INT_MIN;
for(int right = left; right <= left+k-1 && right <= n; right++) {
mn = min(mn,a[right]);
mx = max(mx,a[right]);
}
b[left] = mn;
c[left] = mx;
}
for(int i = 1; i <= n+1-k; i++) {
cout<<b[i]<<" ";
}
cout<<endl;
for(int i = 1; i <= n+1-k; i++) {
cout<<c[i]<<" ";
}
cout<<endl;
return 0;
}
优化:使用更高效的数据结构维护最大值和最小值。
代码2:单调队列
#include <bits/stdc++.h>
#define MX 1000005
using namespace std;
//单调队列模板题
int a[MX];
int main() {
int n,k;
cin>>n>>k;
deque<int> mn_deque,mx_deque;
vector<int> mn,mx;
for(int i = 1; i <= n; i++) {
cin>>a[i];
}
for(int i = 1; i <= n; i++) {
//维护最小值队列
while(!mn_deque.empty() && mn_deque.front() < i - k + 1) {
mn_deque.pop_front();
}
while(!mn_deque.empty() && a[mn_deque.back()] >= a[i]) {
mn_deque.pop_back();
}
mn_deque.push_back(i);
//维护最大值队列
while(!mx_deque.empty() && mx_deque.front() < i - k + 1) {
mx_deque.pop_front();
}
while(!mx_deque.empty() && a[mx_deque.back()] <= a[i]) {
mx_deque.pop_back();
}
mx_deque.push_back(i);
if(i >= k) {
mn.push_back(a[mn_deque.front()]);
mx.push_back(a[mx_deque.front()]);
}
}
for(auto x : mn) {
cout<<x<<" ";
}
cout<<endl;
for(auto x : mx) {
cout<<x<<" ";
}
cout<<endl;
return 0;
}