E11【模板】单调队列 滑动窗口最值
视频链接:E11【模板】单调队列 滑动窗口最值_哔哩哔哩_bilibili
#include <iostream> using namespace std; const int N=1000010; int a[N], q[N]; int main(){ int n, k; scanf("%d%d", &n, &k); for(int i=1; i<=n; i++) scanf("%d", &a[i]); // 维护窗口最小值 int h=1, t=0; //头尾指针初值 for(int i=1; i<=n; i++){ while(h<=t && a[q[t]]>=a[i]) t--; //队尾出队(队列不空且新元素更优) q[++t]=i; //队尾入队(存储下标 方便判断队头出队) if(q[h]<i-k+1) h++; //队头出队(队头元素滑出窗口) if(i>=k) printf("%d ", a[q[h]]); //输出最值 } puts(""); // 维护窗口最大值 h=1, t=0; for(int i=1; i<=n; i++){ while(h<=t && a[q[t]]<=a[i]) t--; q[++t]=i; if(q[h]<i-k+1) h++; if(i>=k) printf("%d ", a[q[h]]); } }
#include <iostream> #include <deque> using namespace std; const int N=1000010; int a[N]; deque<int> q; int main(){ int n, k; scanf("%d%d", &n, &k); for(int i=1; i<=n; i++) scanf("%d", &a[i]); // 维护窗口最小值 q.clear(); //清空队列 for(int i=1; i<=n; i++){ //枚举序列 while(!q.empty() && a[q.back()]>=a[i]) q.pop_back(); //队尾出队(队列不空且新元素更优) q.push_back(i); //队尾入队(存储下标 方便判断队头出队) while(q.front()<i-k+1) q.pop_front(); //队头出队(队头元素滑出窗口) if(i>=k) printf("%d ",a[q.front()]); //使用最值 } puts(""); // 维护窗口最大值 q.clear(); for(int i=1; i<=n; i++){ while(!q.empty() && a[q.back()]<=a[i]) q.pop_back(); q.push_back(i); while(q.front()<i-k+1) q.pop_front(); if(i>=k) printf("%d ",a[q.front()]); } }
#include <iostream> using namespace std; const int N=1000010; int a[N], q[N]; int main(){ int n, k; scanf("%d%d", &n, &k); for(int i=1; i<=n; i++) scanf("%d", &a[i]); // 维护窗口最小值 int h=1, t=0; //清空队列 for(int i=1; i<=n; i++){ //枚举序列 while(h<=t && q[h]<i-k+1) h++; //队头出队(队列不空且队头元素滑出窗口) while(h<=t && a[q[t]]>=a[i]) t--; //队尾出队(队列不空且新元素更优) q[++t]=i; //队尾入队(存储下标 方便判断队头出队) if(i>=k) printf("%d ", a[q[h]]); //使用最值 } puts(""); // 维护窗口最大值 h=1, t=0; for(int i=1; i<=n; i++){ while(h<=t && q[h]<i-k+1) h++; while(h<=t && a[q[t]]<=a[i]) t--; q[++t]=i; if(i>=k) printf("%d ", a[q[h]]); } }
练习:
分步做,把矩形区的最值先按行压缩到到一格存储,后按列压缩到一格存储
枚举行,横向滑动窗口,把每行的窗口最值存储在 maxv[i][j], minv[i][j]
枚举列,竖向滑动窗口,把每列的窗口最值存储在 c[i], d[i]
#include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N=1010, INF=1e9; int n, m, k; int w[N][N], minv[N][N], maxv[N][N]; int q[N], a[N], b[N], c[N], d[N]; // maxv[i][j]:第i行, j-k+1~j列的最大值 // a[i]:第i行, j-k+1~j列的最大值 // c[i]:第i-k+1~i行, j-k+1~j列的最大值 void get_max(int a[], int b[], int m){ int h=1, t=0; for(int i=1; i<=m; i++){ while(h<=t && a[q[t]]<=a[i]) t--; q[++t]=i; if(q[h]<i-k+1) h++; b[i]=a[q[h]]; } } void get_min(int a[], int b[], int m){ int h=1, t=0; for(int i=1; i<=m; i++){ while(h<=t && a[q[t]]>=a[i]) t--; q[++t]=i; if(q[h]<i-k+1) h++; b[i]=a[q[h]]; } } int main(){ scanf("%d%d%d", &n, &m, &k); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d",&w[i][j]); for(int i=1; i<=n; i++){ //枚举行 get_max(w[i],maxv[i],m); //横滑窗口 get_min(w[i],minv[i],m); } int res=INF; for(int j=k; j<=m; j++){ //枚举列 for(int i=1; i<=n; i++){ a[i]=maxv[i][j]; b[i]=minv[i][j]; } get_max(a, c, n); //竖滑窗口 get_min(b, d, n); for(int i=k;i<=n;i++) res=min(res,c[i]-d[i]); } printf("%d\n", res); }