P2216 [HAOI2007]理想的正方形
题目描述
有一个a*b的整数组成的矩阵,现请你从中找出一个n*n的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
a,b<=1000
分析题目: 首先可以想到一个O(a*b*n) 的解法:
我最开始想的办法是预处理递推出矩形中的最大值和最小值,即:用maxv(i,j,k)表示以点(i,j)为左上角的边长为k的矩形中的最大值,然后用递推公式
maxv[i][j] = max(grid[i][j], max(maxv[i+1][j+1], max(maxv[i+1][j], maxv[i][j+1])));
但是只有50分,接下来,有两种优化方法:
1.把n转化为log(n),用RMQ(倍增)
2.单调队列:每一行O(b)处理,然后每一列O(a)处理,复杂度应为O(a*b)
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
#define res register int
#define getchar gc
char buf[1<<18],*fc,*tc;
inline char gc()
{
if(fc==tc)
{
tc=(fc=buf)+fread(buf,1,1<<18,stdin);
if(tc==fc) return EOF;
}
return *fc++;
}
inline int read()
{
int x=0,f=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') f=-1;
while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return f*x;
}
const int N=1005,inf=0x3f3f3f3f;
int a,b,n;
int mp[N][N],X[N][N],x[N][N],Y[N][N],y[N][N];
//大写最大值,小写最小值
//x[i][j]:第i行j~j+n-1的最小值
//y[i][j]:从mp[i~i+n-1][j~j+n-1]的最小值
int Q[N],q[N],h,t,H,T;//head -> tail 单调
inline void solve()
{
for(res i=1 ; i<=a ; ++i)
{
h=t=H=T=Q[1]=q[1]=1;
for(res j=2 ; j<=b ; ++j)
{
while(H<=T&&mp[i][j]>=mp[i][Q[T]]) --T;//维护单调性
while(h<=t&&mp[i][j]<=mp[i][q[t]]) --t;
q[++t]=j; Q[++T]=j;
//维护大小
while(j-Q[H]>=n) ++H;
while(j-q[h]>=n) ++h;
if(j>=n) X[i][j-n+1]=mp[i][Q[H]],x[i][j-n+1]=mp[i][q[h]];
}
}
for(res j=1 ; j+n-1<=b ; ++j)//维护j~j+n-1列
{
h=t=H=T=Q[1]=q[1]=1;
for(res i=2 ; i<=a ; ++i)
{
while(H<=T&&X[i][j]>=X[Q[T]][j]) --T;
while(h<=t&&x[i][j]<=x[q[t]][j]) --t;
Q[++T]=i; q[++t]=i;
while(i-Q[H]>=n) ++H;
while(i-q[h]>=n) ++h;
if(i>=n) Y[i-n+1][j]=X[Q[H]][j],y[i-n+1][j]=x[q[h]][j];
}
}
}
int main()
{
a=read(); b=read(); n=read();
for(res i=1 ; i<=a ; ++i)
for(res j=1 ; j<=b ; ++j) mp[i][j]=read();
solve();
int ans=inf;
for(res i=1 ; i+n-1<=a ; ++i)
for(res j=1 ; j+n-1<=b ; ++j)
ans=min(ans,Y[i][j]-y[i][j]);
printf("%d\n",ans);
return 0;
}