Description
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
Input
第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每行相邻两数之间用一空格分隔。
Output
仅一个整数,为a*b矩阵中所有“n*n正方形区域中的最大整数和最小整数的差值”的最小值。
Sample Input
5 4 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
1 2 5 6
0 17 16 0
16 17 2 1
2 10 2 1
1 2 2 2
Sample Output
1
HINT
问题规模
(1)矩阵中的所有数都不超过1,000,000,000
(2)20%的数据2<=a,b<=100,n<=a,n<=b,n<=10
(3)100%的数据2<=a,b<=1000,n<=a,n<=b,n<=10
题解
暴力的枚举很简单,枚举即可。然后可以考虑减少找最大最小值的时间。可以向《移动的窗户》那样,然后用单调队列维护每一行所有长度为n的子段中的最大最小值。然后在此基础上,竖着类似地做一遍即可。
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<cmath> #include<algorithm> #define MAXN 1005 using namespace std; int n,m,K,a[MAXN][MAXN]; int row[MAXN][MAXN][2],b[MAXN][MAXN][2],q1[MAXN],q2[MAXN]; int ans=1<<30; void init() { scanf("%d%d%d",&n,&m,&K); int i,j; for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%d",&a[i][j]); } void pre() { int i,j,t1,w1,t2,w2; for(i=1;i<=n;i++) {t1=1; w1=0; t2=1; w2=0; memset(q1,0,sizeof(q1)); memset(q2,0,sizeof(q2)); for(j=1;j<=m;j++) {while(t1<=w1&&j-q1[t1]+1>K) t1++; while(t1<=w1&&a[i][q1[w1]]>a[i][j]) w1--; while(t2<=w2&&j-q2[t2]+1>K) t2++; while(t2<=w2&&a[i][q2[w2]]<a[i][j]) w2--; q1[++w1]=j; q2[++w2]=j; row[i][j][0]=a[i][q1[t1]]; row[i][j][1]=a[i][q2[t2]]; } } int x; for(i=1;i<=m;i++) {t1=1,w1=0; t2=1,w2=0; memset(q1,0,sizeof(q1)); memset(q2,0,sizeof(q2)); for(j=1;j<=n;j++) {while(t1<=w1&&j-q1[t1]+1>K) t1++; while(t1<=w1&row[q1[w1]][i][0]>row[j][i][0]) w1--; while(t2<=w2&&j-q2[t2]+1>K) t2++; while(t2<=w2&row[q2[w2]][i][1]<row[j][i][1]) w2--; q1[++w1]=j; q2[++w2]=j; b[j][i][0]=row[q1[t1]][i][0]; b[j][i][1]=row[q2[t2]][i][1]; } } } void find() { int i,j; for(i=K;i<=n;i++) for(j=K;j<=m;j++) ans=min(ans,b[i][j][1]-b[i][j][0]); printf("%d\n",ans); } int main() { freopen("square.in","r",stdin); freopen("square.out","w",stdout); init(); pre(); find(); return 0; }