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<=100
题解:竟然是一个只考察单调队列的题。。
先横向建立单调队列,再在这些横向单调队列的基础上竖向建立单调队列。
然后就好了。
代码:
#include<iostream>
#include<cstdio>
#define N 1010
using namespace std;
struct use{int p,v;}q1[N],q2[N],qmx[N][N],qmn[N][N];
int n,m,k,a[N][N],l1,r1,l2,r2,lmx[N],rmx[N],lmn[N],rmn[N],ans(999999999);
void push(int x,int p,int i){
while(lmx[i]<=rmx[i]&&p-qmx[i][lmx[i]].p>=k) lmx[i]++;
while(lmn[i]<=rmn[i]&&p-qmn[i][lmn[i]].p>=k) lmn[i]++;
while(lmx[i]<=rmx[i]&&x>=qmx[i][rmx[i]].v) rmx[i]--;
while(lmn[i]<=rmn[i]&&x<=qmn[i][rmn[i]].v) rmn[i]--;
qmx[i][++rmx[i]].v=qmn[i][++rmn[i]].v=x;qmx[i][rmx[i]].p=qmn[i][rmn[i]].p=p;
}
void push2(int mx,int mn,int p){
while(l1<=r1&&p-q1[l1].p>=k) l1++;
while(l2<=r2&&p-q2[l2].p>=k) l2++;
while(l1<=r1&&mx>=q1[r1].v) r1--;
while(l2<=r2&&mn<=q2[r2].v) r2--;
q1[++r1].v=mx;q2[++r2].v=mn;q1[r1].p=q2[r2].p=p;
}
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++) lmx[i]=lmn[i]=1;
for (int i=1;i<=m;i++)
for (int j=1;j<=k;j++)push(a[j][i],j,i);
for (int i=k;i<=n;i++){
l1=l2=1;r1=r2=0;
if (i!=k) for (int j=1;j<=m;j++) push(a[i][j],i,j);
for (int j=1;j<=k;j++) push2(qmx[j][lmx[j]].v,qmn[j][lmn[j]].v,j);
ans=min(ans,q1[l1].v-q2[l2].v);
for (int j=k+1;j<=m;j++){
push2(qmx[j][lmx[j]].v,qmn[j][lmn[j]].v,j);ans=min(ans,q1[l1].v-q2[l2].v);
}
}
cout<<ans<<endl;
}