理想的正方形
【题目描述】
一个a*b的矩阵,从中取一个n*n的子矩阵,使所选矩阵中的最大数与最小数的差最小。
思路:
二维的滑动窗口
对于每行:用一个单调队列维护,算出每个长度为n的区间的最大值和最小值,分别存在两个数组fmin和fmax中,fmax[i][j]表示第i行区间[j,j+n-1]的最大值。
对于每列:用一个单调队列维护,算出fmax和fmin数组中纵列每个长度为n的区间的最大值和最小值,分别存在两个数组ffmin和ffmax中,
ffmax[i][j]表示以(i,j)为左上端点的大小为n*n的矩阵中的最大值。
扫一遍ffmax[1~a-n+1][1~b-n+1]和ffmin[1~a-n+1][1~b-n+1]的差,得出ans。
单调队列原理:
以维护最大值为例:
对于每个新加入区间的值,显而易见的是:对于向右移动的“窗口”,即当前长度为n的区间中,若存在data[q[i]]比data[q[j]]大且q[i]>q[j](q[i]表示队列中的第i个元素的编号,data[i]表示编号为i的元素的值),则可以保证q[j]在以后的区间取最大值时是不会产生影响的,我们便可以将q[j]删除,从而得到更加优秀的时间复杂度,所以,当一个新的值入队时,便可以将在其前面入队且值比它小的元素删除。 我们便可以发现可以用一个单调队列维护。 当然,对于不在当前区间内的“老”元素,要把它从队列中删除。
贴C++代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int a,b,n; int g[1001][1001],fmin[1001][1001],fmax[1001][1001],ffmin[1001][1001],ffmax[1001][1001],queue[1001],head,tail,i,j,ans=0x7fffffff; int main() { scanf("%d%d%d",&a,&b,&n); for(i=1;i<=a;i++) for(j=1;j<=b;j++) scanf("%d",&g[i][j]); for(i=1;i<=a;i++) //枚举每行 单调递减求区间最大值 { head=1;tail=0; memset(queue,0,sizeof(queue)); for(j=1;j<n;j++) { while(tail>0&&g[i][queue[tail]]<g[i][j]) tail--; queue[++tail]=j; } for(j=n;j<=b;j++) { while(tail>=head&&g[i][queue[tail]]<g[i][j]) tail--; queue[++tail]=j; if(queue[head]<j-n+1) head++; fmax[i][j-n+1]=g[i][queue[head]]; } } for(i=1;i<=a;i++) //枚举每行 单调递增求区间最小值 { head=1;tail=0; memset(queue,0,sizeof(queue)); for(j=1;j<n;j++) { while(tail>0&&g[i][queue[tail]]>g[i][j]) tail--; queue[++tail]=j; } for(j=n;j<=b;j++) { while(tail>=head&&g[i][queue[tail]]>g[i][j]) tail--; queue[++tail]=j; if(queue[head]<j-n+1) head++; fmin[i][j-n+1]=g[i][queue[head]]; } } for(i=1;i<=b-n+1;i++) //枚举每列 单调递减求区间最大值 { head=1;tail=0; memset(queue,0,sizeof(queue)); for(j=1;j<n;j++) { while(tail>0&&fmax[queue[tail]][i]<fmax[j][i]) tail--; queue[++tail]=j; } for(j=n;j<=a;j++) { while(tail>=head&&fmax[queue[tail]][i]<fmax[j][i]) tail--; queue[++tail]=j; if(queue[head]<j-n+1) head++; ffmax[j-n+1][i]=fmax[queue[head]][i]; } } for(i=1;i<=b-n+1;i++) //枚举每列 单调递增求区间最小值 { head=1;tail=0; memset(queue,0,sizeof(queue)); for(j=1;j<n;j++) { while(tail>0&&fmin[queue[tail]][i]>fmin[j][i]) tail--; queue[++tail]=j; } for(j=n;j<=a;j++) { while(tail>=head&&fmin[queue[tail]][i]>fmin[j][i]) tail--; queue[++tail]=j; if(queue[head]<j-n+1) head++; ffmin[j-n+1][i]=fmin[queue[head]][i]; } } for(i=1;i<=a-n+1;i++) for(j=1;j<=b-n+1;j++) ans=min(ans,ffmax[i][j]-ffmin[i][j]); printf("%d ",ans); return 0; }