单调队列分别维护行与列
具体实现方法:是先用单调队列对每一行的值维护,并将a[][]每个区间的最大值,最小值分别存在X[][]和x[][]中。
那么X[][]与x[][]所存储的分别是1×n的长方形内的最大值,最小值。X[i][j]存储第i行第jj+n-1列的长方形中的最大值。同理,x[i][j]存储第i行第jj+n-1列的长方形中的最小值。
这时再对这两个数组的每一列上的值进行维护,将X[][]中每个区间的的最大值用Y[][]维护,将x[][]中的每个区间的最小值用y[][]维护。那么Y[i][j]存储X[][]中第ii+n-1行第j列的长方形的最大值。同理y[i][j]存储x[][]中第ii+n-1行第j列的长方形的最小值。
故Y[i][j]存储的实为以a[ii+n-1][jj+n-1]中的最大,即以i,j为左上角,边长为n的正方形中的最大值。同理,y[i][j]存储的即以i,j为左上角,边长为n的正方形中的最小值。
#include <cstdio>
using namespace std;
int a,b,n;
int s[1001][1001];//存放矩阵数组
int Sa[1001][1001],sa[1001][1001];//Sa[i][j]表示第i行,第j到j+n-1个数的最大值,sa[i][j]表示第i行,第j到j+n-1个数的最小值
int Sb[1001][1001],sb[1001][1001];//Sb[i][j]表示第j列,第i到i+n-1行的数的最大值,sb[i][j]表示第j列,第i到i+n-1行的数的最小值
int QmQ[1001],QnQ[1001];
int BQ,bq,FQ,fq,ans;
//QmQ数组 单调队列,维护最大值,最大值在队首
//相对的QnQ数组 单调队列,维护最小值,最小值在队首
int min(int c,int d){return c>d?d:c;}
int main(){
scanf("%d%d%d",&a,&b,&n);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)
scanf("%d",&s[i][j]);
for(int i=1;i<=a;i++){
BQ=bq=FQ=fq=QmQ[1]=QnQ[1]=1;//初始化指向队首和队尾的位置,以及队首的位置对应的数的位置
for(int j=2;j<=b;j++){
while(s[i][j]>=s[i][QmQ[BQ]]&&FQ<=BQ)BQ--;
while(s[i][j]<=s[i][QnQ[bq]]&&fq<=bq)bq--;
/*
为第i行第j个数在队列中找位置,从队尾的位置开始往前找,
如第一个while,是为当前这个数找位置,以维护当前的 单调队列 的最大值
如果当前数比队尾的数大,则BQ--。也就是让当前数排在,队列中比他大的数的后面
相对的,第二个while循环同理
*/
BQ++;bq++;QmQ[BQ]=j;QnQ[bq]=j;
//找到了相应的位置,将当前数的下标,放在队列的相应位置
while(j-QmQ[FQ]>=n)FQ++;
while(j-QnQ[fq]>=n)fq++;
//上面这两个while循环为了调整QmQ[FQ],QnQ[fq]相对于j的位置, 也就是最大值和最小值的位置与j 相对位置 不能超过n
//如j=5,QmQ[FQ]=2,也就是当前最大值的单调队列的队首,位置是2, 而要求的是j-n+1到j的最大值,所以要缩小距离,要在队列的后面找合适的数的位置
if(j>=n)Sa[i][j-n+1]=s[i][QmQ[FQ]],sa[i][j-n+1]=s[i][QnQ[fq]];
//这里一定要是j>=n,因为只有这样,j-n+1 才不为负数!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
}
for(int i=1;i<=(b-n+1);i++){//第一个循环与Sa数组的定义有关
BQ=bq=FQ=fq=QmQ[1]=QnQ[1]=1;
for(int j=2;j<=a;j++){
while(Sa[j][i]>=Sa[QmQ[BQ]][i]&&FQ<=BQ)BQ--;
while(sa[j][i]<=sa[QnQ[bq]][i]&&fq<=bq)bq--;
BQ++;bq++;QmQ[BQ]=j;QnQ[bq]=j;
while(j-QmQ[FQ]>=n)FQ++;
while(j-QnQ[fq]>=n)fq++;
if(j>=n)Sb[j-n+1][i]=Sa[QmQ[FQ]][i],sb[j-n+1][i]=sa[QnQ[fq]][i];
}
}
ans=0x3f3f3f3f;
for(int i=1;i<=(a-n+1);i++)
for(int j=1;j<=(b-n+1);j++)
ans=min(ans,Sb[i][j]-sb[i][j]);
printf("%d",ans);
return 0;
}