理想的正方形
二维单调队列
这道题目其实和dp其实没什么关系了,主要是一个二维单调队列求最值的问题
题意:求一个二维矩阵中,所有边长为n的正方形中
最大值
−
最小值
最大值-最小值
最大值−最小值,最大为多少。如下图,即求所有这样蓝色正方形中,
最大值
−
最小值
最大值-最小值
最大值−最小值的最大的值。
[外链图片转存中…(img-jA2pswOO-1722327180398)]
最暴力的想法是,枚举每一个正方形,然后再求出正方形中的最大值和最小值。这样时间复杂度是 O ( n 2 a b ) O(n^2ab) O(n2ab)。显然会超时,接下来考虑如何去优化这个问题。
最大值和最小值是同一个问题,这里只考虑如何求最大值,最小值只需要对称去做即可。
思路参考y总视频
对于这个二维的问题我们不妨先从一维入手。
对于一维问题,我们可以用单调队列去求,这可以先把二维的每一行都看成单独的序列,先用单调队列求出最值存储在每一行的右端点。如下图。
每一行长度为n的滑动窗口的最大值都存储在同一列上。
这时只需要再对上面结果矩阵~~(求出每一行长度为n的滑动窗口最大值的矩阵)~~按列方向再求一遍长度为n的滑动窗口操作,这样得到的新的矩阵就是边长为n的正方形的最大值矩阵,如下图。
最后再套用滑动窗口模板对最大值、最小值求两遍,就能得到所有边长为n的正方行的最大值、最小值。
再分析一下时间复杂度,瓶颈在求所有边长为n的正方形的最大值、最小值。
再求最大值的过程中是分别对每一行都做一遍滑动窗口,做一遍滑动窗口的时间复杂度是
O
(
n
)
O(n)
O(n),这里需要做
n
n
n,所以时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)。
那么总的时间复杂度也就是
O
(
n
2
)
O(n^2)
O(n2)。
#include<bits/stdc++.h>
#define rep(x,y,z) for(int x=(y);x<=z;x++)
#define fep(x,y,z) for(int x=(y);x>=z;x--)
#define db double
#define ll long long
using namespace std;
const int N=1010,M=N<<1;
int n,m,k;
int w[N][N],minv[N][N],maxv[N][N],a[N],b[N],c[N],d[N];
int q[N],hh,tt;
void get_max(int a[],int f[],int n)
{
hh=0,tt=-1;
rep(i,1,n)
{
while(hh<=tt&&i-q[hh]>k-1) hh++;//窗口长度大于k,队头出队
while(hh<=tt&&a[i]>=a[q[tt]]) tt--;//去除队列中冗余元素
q[++tt]=i;//入队
f[i]=a[q[hh]];//更新滑动窗口的答案
}
}
void get_min(int a[],int f[],int n)
{
hh=0,tt=-1;
rep(i,1,n)
{
while(hh<=tt&&i-q[hh]>k-1) hh++;//窗口长度大于k,队头出队
while(hh<=tt&&a[i]<=a[q[tt]]) tt--;//去除队列中冗余元素
q[++tt]=i;//入队
f[i]=a[q[hh]];//更新滑动窗口的答案
}
}
void solve()
{
cin>>n>>m>>k;
rep(i,1,n) rep(j,1,m) cin>>w[i][j];
rep(i,1,n)//对每一行做滑动窗口,求出最值
{
get_min(w[i],minv[i],m);
get_max(w[i],maxv[i],m);
}
ll res=1e18;
rep(i,k,m)//对每一列做滑动窗口,求出最值的最值
{
rep(j,1,n)//抠出来每一列
{
a[j]=minv[j][i];
b[j]=maxv[j][i];
}
get_min(a,c,n);
get_max(b,d,n);
rep(j,k,n) res=min(1ll*d[j]-c[j],res);
}
cout<<res<<'\n';
}
int main()
{
// freopen("1.in","r",stdin);
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
solve();
return 0;
}