1047: [HAOI2007]理想的正方形
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 4137 Solved: 2326
Description
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值
的差最小。
Input
第一行为3个整数,分别表示a,b,n的值第二行至第a+1行每行为b个非负整数,表示矩阵中相应位置上的数。每
行相邻两数之间用一空格分隔。
100%的数据2<=a,b<=1000,n<=a,n<=b,n<=1000
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
Sample Output
1
解析:
先把每个位置往左 n 个单位中的最大最小值算出来(用单调队列优化),然后对于同一列维护一个单调队列,这样就能快速得到一个矩阵的最大值、最小值了。
令f[ i ][ j ][ 0 ]表示(i , j)这个位置往左 n 个单位中最小值,f[ i ][ j ][ 1 ]为最大值,f[ i ][ j ][ 2 ]表示以(i , j)这个位置为结尾的矩形中的最小值,f[ i ][ j ][ 3 ]为最大值,详细过程见代码。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=1010;
const int inf=1e9;
int n,m,k,ans=inf,head,tail;
int num[Max][Max],f[Max][Max][4],p[Max<<3],q[Max<<3];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline int mn(int x,int y){return x > y ? y : x;}
inline void pre()
{
for(int i=1;i<=n;i++)
{
head = tail = 0;
for(int j=1;j<=n;j++) p[j]=0;
for(int j=1;j<=m;j++)
{
while(head <= tail && p[head] < j-k+1) head++;
while(head <= tail && num[i][j] <= num[i][p[tail]]) tail--;
p[++tail] = j;
f[i][j][0] = num[i][p[head]];
}
head = tail = 0;
for(int j=1;j<=n;j++) p[j]=0;
for(int j=1;j<=m;j++)
{
while(head <= tail && p[head] < j-k+1) head++;
while(head <= tail && num[i][j] >= num[i][p[tail]]) tail--;
p[++tail] = j;
f[i][j][1] = num[i][p[head]];
}
}
}
inline void solve()
{
for(int j=k;j<=m;j++)
{
head = tail = 0;
for(int i=1;i<=n;i++) q[i]=0;
for(int i=1;i<=n;i++)
{
while(head <= tail && q[head] < i-k+1) head++;
while(head <= tail && f[i][j][0] <= f[q[tail]][j][0]) tail--;
q[++tail] = i;
f[i][j][2] = f[q[head]][j][0];
}
head = tail = 0;
for(int i=1;i<=n;i++) q[i]=0;
for(int i=1;i<=n;i++)
{
while(head <= tail && q[head] < i-k+1) head++;
while(head <= tail && f[i][j][1] >= f[q[tail]][j][1]) tail--;
q[++tail] = i;
f[i][j][3] = f[q[head]][j][1];
}
}
}
int main()
{
n=get_int(),m=get_int(),k=get_int();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
num[i][j] = get_int();
f[i][j][0] = f[i][j][2] = inf;
}
pre();
solve();
for(int i=k;i<=n;i++)
for(int j=k;j<=m;j++) ans=mn(ans,f[i][j][3]-f[i][j][2]);
cout<<ans<<"\n";
return 0;
}