题意:给你一个n*m的矩阵,让你找到一个最大的L值,满足边长为L的正方形子矩阵中最大最小值差值小于等于G
解析:一个显然的做法是使用二维数据结构,然而二维线段树和二维树状数组容易写炸,二维RMQ被卡了空间,于是只能选择滚动数组dp或者单调队列进行求解。
由于笔者很少利用单调队列解题,正好通过这道题补一补单调队列的知识。
对于单调队列,我们这样子来定义:
- 1、维护区间最值
- 2、去除冗杂状态 对于区间中的两个元素a[i],a[j](假设现在在求最大值)
若 j>i且a[j]>=a[i] ,a[j]比a[i]还大而且还在后面(目前a[j]留在队列肯定比a[i]有用)- 3、保持队列单调,最大值是单调递减序列,最小值反之
- 4、最优选择在队首
单调队列能够在O(n)的复杂度内处理出所有区间长度为m的区间最大/最小值
那么再使用悬线法思想,先用单调队列把每一列的状态压缩为一行,再对这一行使用单调队列即可处理出m*m的矩阵的最大最小值,然后我们再二分答案,对每一次的mid在O(n*m)的复杂度内即可判断是否符合条件,整体复杂度O(nmlog(min(n,m))
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,G;
int a[505][505];
int dmax[505][505];
int dmin[505][505];
int rmax[505][505];
int rmin[505][505];
int work(int L)
{
deque<pair<int,int> > mmax;
deque<pair<int,int> > mmin;
for(int j=1;j<=m;j++)
{
mmax.clear(),mmin.clear();
for(int i=1;i<L;i++)
{
while(!mmin.empty()&&mmin.back().first>=a[i][j]) mmin.pop_back();
mmin.push_back(make_pair(a[i][j],i));
while(!mmax.empty()&&mmax.back().first<=a[i][j]) mmax.pop_back();
mmax.push_back(make_pair(a[i][j],i));
}
for(int i=L;i<=n;i++)
{
while(!mmin.empty()&&mmin.back().first>=a[i][j]) mmin.pop_back();
mmin.push_back(make_pair(a[i][j],i));
while(mmin.front().second+L<=i) mmin.pop_front();
dmin[i-L+1][j]=mmin.front().first;
while(!mmax.empty()&&mmax.back().first<=a[i][j]) mmax.pop_back();
mmax.push_back(make_pair(a[i][j],i));
while(mmax.front().second+L<=i) mmax.pop_front();
dmax[i-L+1][j]=mmax.front().first;
}
}
for(int i=1;i<=n;i++)
{
mmax.clear(),mmin.clear();
for(int j=1;j<L;j++)
{
while(!mmin.empty()&&mmin.back().first>=dmin[i][j]) mmin.pop_back();
mmin.push_back(make_pair(dmin[i][j],j));
while(!mmax.empty()&&mmax.back().first<=dmax[i][j]) mmax.pop_back();
mmax.push_back(make_pair(dmax[i][j],j));
}
for(int j=L;j<=m;j++)
{
while(!mmin.empty()&&mmin.back().first>=dmin[i][j]) mmin.pop_back();
mmin.push_back(make_pair(dmin[i][j],j));
while(mmin.front().second+L<=j) mmin.pop_front();
rmin[i][j-L+1]=mmin.front().first;
while(!mmax.empty()&&mmax.back().first<=dmax[i][j]) mmax.pop_back();
mmax.push_back(make_pair(dmax[i][j],j));
while(mmax.front().second+L<=j) mmax.pop_front();
rmax[i][j-L+1]=mmax.front().first;
}
}
for(int i=1;i<=n-L+1;i++)
for(int j=1;j<=m-L+1;j++)
if(rmax[i][j]-rmin[i][j]<=G)
return 1;
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&G);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
int l=1,r=min(n,m);
int ans=1;
while(l<=r)
{
int mid=l+r>>1;
if(work(mid))
{
ans=mid;
l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
return 0;
}