单调队列专题

目录

1,滑动窗口;

2,印刷广告;

 3,理想的正方形(二维滑动窗口模型);


单调队列存储的是一段区间的单调递增或递减的子序列的下标值(为什么是下标值后面会说);因此取区间最值只取队头即可做到;

存储下标是为了方便将不属于本区间的队头去掉;利用q[hh]<i-m+1,hh++;

注意,队列是这样的,hh,。。。,tt;左边是队头,右边是队尾;

1,滑动窗口;

单调队列模板题,核心代码给出了注释; 

int n,k;
int a[N];
int q[N],hh,tt=-1;
signed main()
{
    quick_cin();
    cin>>n>>k;
    rep2(i,1,n)cin>>a[i];
    rep2(i,1,n)
    {
        while(hh<=tt&&q[hh]<i-k+1)hh++;//不属于本区间的队头弹出;
        while(hh<=tt&&a[i]<=a[q[tt]])tt--;//新进来的元素比队列里的元素还小,那么队列里的元素不可能是区间最小值,直接从队尾抛弃; 
        q[++tt]=i;//新进来的元素进入 队列;
        if(i>=k)cout<<a[q[hh]]<<" ";
    }
    hh=0,tt=-1;
    memset(q,0,q);
    cout<<endl;
//同上;
    rep2(i,1,n)
    {
        while(hh<=tt&&q[hh]<i-k+1)hh++;
        while(hh<=tt&&a[i]>=a[q[tt]])tt--;
        q[++tt]=i;
        if(i>=k)cout<<a[q[hh]]<<" ";
    }
    return 0;
}
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register int i=(a);i<(n);++i) 
#define rep2(i,a,n) for(register int i=(a);i<=(n);++i) 
#define per1(i,n,a) for(register int i=(n);i>(a);i--) 
#define per2(i,n,a) for(register int i=(n);i>=(a);i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define memset(a,i,b) memset((a),(i),sizeof (b))
#define memcpy(a,i,b) memcpy((a),(i),sizeof (b))
#define pro_q priority_queue
#define pb push_back
#define pf push_front
#define endl "\n"
#define lowbit(m) ((-m)&(m))
#define YES cout<<"YES\n"
#define NO cout<<"NO\n"
#define Yes cout<<"Yes\n"
#define No cout<<"No\n"
#define yes cout<<"yes\n"
#define no cout<<"no\n"
#define yi first
#define er second
#define INF 0x3f3f3f3f
#define tulun int e[N],ne[N],h[N],w[N],idx;
#define add2(a,b) e[idx]=b,ne[idx]=h[a],h[a]=idx++;
#define add3(a,b,c) w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
#define T_solve() int T;cin>>T;while(T--)solve();
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<pair<int,int>,pair<int,int>> PIIII;
typedef double dob;
const int N=1e6+10;
int n,k;
int a[N];
int q[N],hh,tt=-1;
signed main()
{
    quick_cin();
    cin>>n>>k;
    rep2(i,1,n)cin>>a[i];
    rep2(i,1,n)
    {
        while(hh<=tt&&q[hh]<i-k+1)hh++;
        while(hh<=tt&&a[i]<=a[q[tt]])tt--;
        q[++tt]=i;
        if(i>=k)cout<<a[q[hh]]<<" ";
    }
    hh=0,tt=-1;
    memset(q,0,q);
    cout<<endl;
    rep2(i,1,n)
    {
        while(hh<=tt&&q[hh]<i-k+1)hh++;
        while(hh<=tt&&a[i]>=a[q[tt]])tt--;
        q[++tt]=i;
        if(i>=k)cout<<a[q[hh]]<<" ";
    }
    return 0;
}

2,印刷广告;

 思路:求出每个数向左和向右能拓展的最长长度;

维护一个单调递增的队列,每次都从队尾弹出到第一个小于当前高度的数,这样取到的下标是极限长度;

这里我用了q[-1]=0这个特点,这样就可以使队伍为空时,即它可以拓展到数组的最边界也能成立;(但会导致不能使用倒循环来求拓展右边界,因为q[0]=n,所以在我reverse h后才求对;后续会改善代码);

 3,理想的正方形(二维滑动窗口模型);

题意:

 思路:

我们先求出在行中,每个长度为n的长方形的最值,并存储到最右边的端点;

 

 大概就是这样,第一行中,每个红色的点存储的就是它左边3个长度的最值;

那我求一个3*3的正方形的最值的时候,其实也就是对列方向上这个存储最值的三个长度的矩形取个最值;如图所示;

 

 因此,就可以把二维的问题转化为两次一维的问题,先横着求一边行的最值, 在纵着对列求一遍最值;两次操作处理完后,正方形的最值就储存在它的右下角;遍历所有右下角即可得到最大值减去最小值;

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rep1(i,a,n) for(register int i=(a);i<(n);++i) 
#define rep2(i,a,n) for(register int i=(a);i<=(n);++i) 
#define per1(i,n,a) for(register int i=(n);i>(a);i--) 
#define per2(i,n,a) for(register int i=(n);i>=(a);i--)
#define quick_cin() cin.tie(0),cout.tie(0),ios::sync_with_stdio(false)
#define memset(a,i,b) memset((a),(i),sizeof (b))
#define memcpy(a,i,b) memcpy((a),(i),sizeof (b))
#define pro_q priority_queue
#define pb push_back
#define pf push_front
#define endl "\n"
#define lowbit(m) ((-m)&(m))
#define YES cout<<"YES\n"
#define NO cout<<"NO\n"
#define Yes cout<<"Yes\n"
#define No cout<<"No\n"
#define yes cout<<"yes\n"
#define no cout<<"no\n"
#define yi first
#define er second
#define INF 0x3f3f3f3f
#define tulun int e[N],ne[N],h[N],w[N],idx;
#define add2(a,b) e[idx]=b,ne[idx]=h[a],h[a]=idx++;
#define add3(a,b,c) w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
#define T_solve() int T;cin>>T;while(T--)solve();
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef double dob;
const int N=1e3+10;
int row_min[N][N],row_max[N][N];
int n,m,k;
int w[N][N];
int q[N];
void get_min(int a[],int b[],int tot)
{
    int hh=0,tt=-1;
    rep2(i,1,tot)
    {
        while(hh<=tt&&q[hh]<=i-k)hh++;
        while(hh<=tt&&a[i]<=a[q[tt]])tt--;
        q[++tt]=i;
        b[i]=a[q[hh]];
    }
}
void get_max(int a[],int b[],int tot)
{
    int hh=0,tt=-1;
    rep2(i,1,tot)
    {
        while(hh<=tt&&q[hh]<=i-k)hh++;
        while(hh<=tt&&a[i]>=a[q[tt]])tt--;
        q[++tt]=i;
        b[i]=a[q[hh]];
    }
}
int ans[N],col_min[N],col_max[N];
PII zuizhi[N][N];
signed main()
{
    quick_cin();
    cin>>n>>m>>k;
    rep2(i,1,n)
    rep2(j,1,m)cin>>w[i][j];
    rep2(i,1,n)
    {
        get_min(w[i],row_min[i],m);
        get_max(w[i],row_max[i],m);
    }
    rep2(j,k,m)
    {
        rep2(i,1,n)ans[i]=row_min[i][j];
        get_min(ans,col_min,n);
        rep2(i,1,n)ans[i]=row_max[i][j];
        get_max(ans,col_max,n);
        rep2(i,1,n)zuizhi[i][j]={col_max[i],col_min[i]};
    }
    int res=INT_MAX;
    rep2(i,k,n)
    rep2(j,k,m)
    res=min(res,zuizhi[i][j].first-zuizhi[i][j].second);
    cout<<res;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dull丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值