Introduction
给你一个
N∗M
的矩阵,给你T组询问,每组询问给定一个子矩阵,求一个最大的正方形的边长,使这个正方形内的所有元素都为
1
且这个正方形在子矩阵内.
数据范围
Solution
这道题很显然是DP。
设
Fi,j
表示以(i,j)为右下角,符合题意的最大边长。
那么转移方程为
Fi,j=max(Fi−1,j,Fi,j−1,Fi−1,j−1)(当且仅当Ai,j=1)
可是这样远远不能AC.
这个时候我们想到了RMQ算法(二维的)。
我们设 rmqi,j,k,l 为当前以 (k−2i+1,l−2j+1) 为右下角, (k,l) 为右下角的正方形符合题意的最大边长。
rmqi,j,k,l 由之前的3个小矩阵更新。
所以转移方程为
rmqi,j,k,l=max(rmqi−1,j,k−2i,l,rmqi,j−1,k,l−2j,rmqi−1,j−1,k−2i,l−2j)
特殊情况:
①i=0,那么 rmqi,j,k,l=max(rmqi,j−1,k,l−2j)
②j=0,那么 rmqi,j,k,l=max(rmqi−1,j,k−2i,l)
询问
对于询问,我们只需要去求4个矩阵的最大值就好了^_^
m1=log2(x2−x1+1),m2=log2(y2−y1+1)
ans=max(rmq[m1][m2][x2][y2],rmq[m1][m2][x1+2m1−1][y1+2m2−1]),rmq[m1][m2][x2][y1+2m2−1],rmq[m1][m2][x1+2m1−1][y2]))
然后ans≥mid就合法,否则不合法。
Code
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define N 1003
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int _2[11],rmq[10][10][N][N];
int i,j,k,l,r,mid,q,n,m,temp,X1,Y1,X2,Y2,ans;
inline int read()
{
int data=0;
char ch=0;
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data;
}
bool ok(int X1,int Y1,int X2,int Y2,int mid)
{
int lx=log2(X2-X1+1),ly=log2(Y2-Y1+1);
ans=max(max(rmq[lx][ly][X2][Y2],rmq[lx][ly][X1+_2[lx]-1][Y1+_2[ly]-1]),
max(rmq[lx][ly][X2][Y1+_2[ly]-1],rmq[lx][ly][X1+_2[lx]-1][Y2]));
if (ans>=mid) return 1;return 0;
}
int main()
{
_2[0]=1;fo(i,1,10) _2[i]=_2[i-1]*2;
n=read();m=read();
fo(i,1,n)
fo(j,1,m) rmq[0][0][i][j]=read();
fo(i,2,n)
fo(j,2,m)
if (rmq[0][0][i][j])
rmq[0][0][i][j]=min(min(rmq[0][0][i-1][j],rmq[0][0][i][j-1]),rmq[0][0][i-1][j-1])+1;
fo(i,0,log2(n))
fo(j,0,log2(m))
if (!i && !j) continue;
else fo(k,_2[i],n)
fo(l,_2[j],m)
{
if (!i) rmq[i][j][k][l]=max(rmq[i][j-1][k][l],rmq[i][j-1][k][l-_2[j-1]]);else
if (!j) rmq[i][j][k][l]=max(rmq[i-1][j][k][l],rmq[i-1][j][k-_2[i-1]][l]);else
rmq[i][j][k][l]=max(max(rmq[i-1][j][k][l],rmq[i-1][j][k-_2[i-1]][l]),
max(rmq[i][j-1][k][l],rmq[i][j-1][k][l-_2[j-1]]));
}
q=read();
fo(i,1,q)
{
X1=read(),Y1=read(),X2=read(),Y2=read();
l=0;r=min(X2-X1+1,Y2-Y1+1);
while (l<r)
{
if (l==r-1)
{
if (ok(X1+r-1,Y1+r-1,X2,Y2,r)) l=r;
break;
}
mid=(l+r)/2;
if (ok(X1+mid-1,Y1+mid-1,X2,Y2,mid)) l=mid;else r=mid-1;
}
printf("%d\n",l);
}
}