1047: [HAOI2007]理想的正方形
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1587 Solved: 835
[ Submit][ Status]
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
Source
[题解] 这道题,最初我想卡,可惜失败了;
一开始,我想,可以 枚举行O(a),然后用2*n个单调队列维护 以这一行为底的大每一列的最最小值,但是觉得这样做有许多是重复做的工作,也很容易TLE,所以就进一步思考。
我们考虑得到 i, j ,求出以(i,j)为右下角的n*n的矩阵的最大最小值。这也可以用单调队列实现,具体可以看代码。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxnn 1800
#define inf 2147483641
int a,b,n,ll[maxnn],rr[maxnn],f[maxnn],w[maxnn][maxnn],h[maxnn][maxnn];
int Min[maxnn][maxnn],Max[maxnn][maxnn],q[maxnn][maxnn],ans;
inline void read(int &x)
{
x=0;char ch;int ff=1;
while(ch=getchar(),(ch>'9' || ch<'0')&& ch!='-');
if(ch=='-')ff=-1,ch=getchar();
while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
x*=ff;
}
inline void getMin()
{
for(int i=1;i<=b;i++)ll[i]=1,rr[i]=0;
for(int i=1,l,r;i<=a;i++)
{
l=1;r=f[0]=0;
for(int j=1;j<=b;j++)
{
while(l<=r && f[l]+n<=j)l++;
while(l<=r && w[i][j]<w[i][f[r]])r--;
f[++r]=j; q[i][j]=f[l];
}
for(int j=1;j<=b;j++)
{
while(ll[j]<=rr[j] && h[j][ll[j]]+n<=i)ll[j]++;
while(ll[j]<=rr[j] && w[h[j][rr[j]]][q[h[j][rr[j]]][j]]>w[i][q[i][j]])rr[j]--;
h[j][++rr[j]]=i;
Min[i][j]=w[h[j][ll[j]]][q[h[j][ll[j]]][j]];
}
}
}
inline void getMax()
{
for(int i=1;i<=b;i++)ll[i]=1,rr[i]=0;
for(int i=1,l,r;i<=a;i++)
{
l=1;r=f[0]=0;
for(int j=1;j<=b;j++)
{
while(l<=r && f[l]+n<=j)l++;
while(l<=r && w[i][j]>w[i][f[r]])r--;
f[++r]=j; q[i][j]=f[l];
}
for(int j=1;j<=b;j++)
{
while(ll[j]<=rr[j] && h[j][ll[j]]+n<=i)ll[j]++;
while(ll[j]<=rr[j] && w[h[j][rr[j]]][q[h[j][rr[j]]][j]]<w[i][q[i][j]])rr[j]--;
h[j][++rr[j]]=i;
Max[i][j]=w[h[j][ll[j]]][q[h[j][ll[j]]][j]];
}
}
}
int main()
{
read(a);read(b);read(n);
for(int i=1;i<=a;i++)
for(int j=1;j<=b;j++)read(w[i][j]);
ans=inf;
getMin();getMax();
for(int i=n;i<=a;i++)
for(int j=n;j<=b;j++)ans=min(ans,Max[i][j]-Min[i][j]);
printf("%d\n",ans);
return 0;
}