题目描述
题解
由题目可知只有每一部分的最大值和最小值是有价值的。
单调队列只能处理一维的情况,二维的不好处理。那么我们可以考虑一维一维处理,比如说,先做列再做行。
可以将每一列每一个长度为n的区间用单调队列处理出来最大值和最小值,然后缩成一个点。
然后将每一行缩成的那些点每一个长度为n的区间再用单调队列处理出来最大值和最小值,这样就相当于求出了n*n的矩形的最大值和最小值统计答案即可。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 2005
#define inf 1000000001
int n,m,k,lmax,rmax,lmin,rmin,ans=inf;
int a[N][N];
struct hp{int Max,Min;}b[N][N];
struct hq{int id,val;}qmax[N],qmin[N];
void push(int i,int val)
{
while (lmax<rmax&&qmax[rmax].val<val) --rmax;
qmax[++rmax].id=i,qmax[rmax].val=val;
while (lmin<rmin&&qmin[rmin].val>val) --rmin;
qmin[++rmin].id=i,qmin[rmin].val=val;
}
hp pop(int i)
{
while (lmax<rmax&&qmax[lmax+1].id<i) ++lmax;
int Max=qmax[lmax+1].val;
while (lmin<rmin&&qmin[lmin+1].id<i) ++lmin;
int Min=qmin[lmin+1].val;
return (hp){Max,Min};
}
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",&a[i][j]);
for (int i=1;i<=m;++i)
{
lmax=rmax=lmin=rmin=0;
for (int j=1;j<=k;++j) push(j,a[j][i]);
b[1][i]=pop(1);
for (int j=k+1;j<=n;++j)
{
push(j,a[j][i]);
b[j-k+1][i]=pop(j-k+1);
}
}
for (int i=1;i<=n-k+1;++i)
{
lmax=rmax=lmin=rmin=0;
for (int j=1;j<=k;++j) push(j,b[i][j].Max),push(j,b[i][j].Min);
hp now=pop(1);ans=min(ans,now.Max-now.Min);
for (int j=k+1;j<=m;++j)
{
push(j,b[i][j].Max);push(j,b[i][j].Min);
now=pop(j-k+1);ans=min(ans,now.Max-now.Min);
}
}
printf("%d\n",ans);
}