E11【模板】单调队列 滑动窗口最值

视频链接:E11【模板】单调队列 滑动窗口最值_哔哩哔哩_bilibili

 

 

Luogu P1886 滑动窗口 /【模板】单调队列

#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]]);
  }
}

 

练习:

Luogu P1440 求m区间内的最小值

Luogu P2032 扫描

Luogu P2216 [HAOI2007]理想的正方形

分步做,把矩形区的最值先按行压缩到到一格存储,后按列压缩到一格存储

枚举行,横向滑动窗口,把每行的窗口最值存储在 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);
}

 

posted @ 2023-04-10 09:24  董晓  阅读(855)  评论(0编辑  收藏  举报