P2216 [HAOI2007]理想的正方形
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; }