时间复杂度O(NM) 空间复杂度O(NM)

定义

有效竖线:

除了两个端点外,不覆盖任何一个障碍点的竖直线段。

悬线:上端覆盖了一个障碍点或者到达整个矩形上边界的有效线段。

每个悬线都与它底部的点一一对应,矩形中的每一个点(矩形顶部的点除外)都对应了一个悬线。

悬线的个数=(N-1)*M;

如果把一个极大子矩形按照横坐标的不同切割成多个与y轴平行的线段,

那么其中至少有一个悬线。

如果把一个悬线向左右两个方向尽可能的移动,

那么就得到了一个矩形,我们称它为悬线对应的矩形。

悬线对应的矩形不一定是极大子矩形,因为下边界可能还可以向下扩展。

设计算法:

原理:所有悬线对应矩形的集合一定包含了极大子矩形的集合。

通过枚举所有的悬线,找出所有的极大子矩形。

算法规模:

悬线个数=(N-1)×M

极大子矩形个数≤悬线个数

具体方法:

设 H[i,j]为点(i,j)对应的悬线的长度。

L[i,j]为点(i,j)对应的悬线向左最多能够移动到的位置。

R[i,j]为点(i,j)对应的悬线向右最多能够移动到的位置。

!考虑点(i,j)对应的悬线与点(i-1,j)对应的悬线的关系

(递推思想):

如果(i-1,j)为障碍点,那么,如图所示,(i,j)对应的悬线长度1,

左右能移动到的位置是整个矩形的左右边界。

即 H[i,j]=1,

?? L[i,j]=0,R[i,j]=m

如果(i-1,j)不是障碍点,

那么,如图所示,(i,j)对应的悬线长度为(i-1,j)对应的悬线长度+1。

即 H[i,j]=H[i-1,j]+1

如果(i-1,j)不是障碍点,

那么,如图所示,(i,j)对应的悬线左右能移动的位置要在(i-1,j)的基础上变化。

L[i,j]=max(L[i-1,j],L[i][j])

即(i-1,j)左边第一个障碍点的位置同理,也可以得到R[i,j]的递推式

R[i,j]=min(R[i][j],R[i-1,j]) 即(i-1,j)右边第一个障碍点的位置

P4147 玉蟾宫

洛谷链接: https://www.luogu.org/problemnew/show/P4147

My AC Code:

include

using namespace std;

const int maxn = 1005;

int map[maxn][maxn];

//map数组,0表示当前位置不能用,1表示当前位置可以用

int l[maxn][maxn],r[maxn][maxn],up[maxn][maxn];

//l[i][j],r[i][j]分别表示(i,j)坐标的点,左,右能到达的最远的点的纵坐标 也就是j

//up[i][j] 表示当前点(i,j) 距离上界的距离

//l和r数组一开始初始化为l[i][j]=r[i][j]=j; 因为 一开始最远只能到达 自己当前位置

//然后up数组一开始初始化为1,代表仅仅是当前位置的高度

int n,m,ans;

char c;

int max(int a,int b){return a>b?a:b;}

int min(int a,int b){return a>b?b:a;}

int main(){

scanf(“%d%d”,&n,&m);

for(int i=1;i<=n;i++){

for(int j=1;j<=m;j++){

scanf(” %c”,&c);

if(c==’F’)map[i][j]=1;

else map[i][j]=0;

l[i][j]=r[i][j]=j;

up[i][j]=1;

}

}

for(int i=1;i<=n;i++)
    for(int j=2;j<=n;j++)
        if(map[i][j]&&map[i][j-1])l[i][j]=l[i][j-1];
/*处理l数组,从左往右推*/

for(int i=1;i<=n;i++)
    for(int j=m-1;j>=1;j--)
        if(map[i][j]&&map[i][j+1])r[i][j]=r[i][j+1];
/*处理r数组,从右往左推*/

for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
        if(i>1&&map[i][j]&&map[i-1][j]){
            l[i][j]=max(l[i][j],l[i-1][j]);
            r[i][j]=min(r[i][j],r[i-1][j]);
            up[i][j]=up[i-1][j]+1;
            }
        ans=max(ans,(r[i][j]-l[i][j]+1)*up[i][j]);
        /*一定要r[i][j]-l[i][j]+1,不+1的话,会错误,因为这样会少计算1的距离,具体可以举例子*/
    }
}
printf("%d",3*ans);

return 0;

}

P1169 [ZJOI2007]棋盘制作

洛谷链接:https://www.luogu.org/problemnew/show/P1169

My AC Code:

/题目要求01交错,所以”!=”即可/

include

include

using namespace std;

const int maxn = 2005;

int n,m,ans,an2,x,mm[maxn][maxn];

int l[maxn][maxn],r[maxn][maxn],up[maxn][maxn];

int min(int a,int b){return a>b?b:a;}

int max(int a,int b){return a>b?a:b;}

int main(){

scanf(“%d%d”,&n,&m);

for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++){
        scanf("%d",&mm[i][j]);
        l[i][j]=r[i][j]=j;up[i][j]=1;
    }

//预处理左边界
for(int i=1;i<=n;i++)
    for(int j=2;j<=m;j++)
        if(mm[i][j]!=mm[i][j-1])l[i][j]=l[i][j-1];

//预处理右边界
for(int i=1;i<=n;i++)
    for(int j=m-1;j>=1;j--)
        if(mm[i][j+1]!=mm[i][j])r[i][j]=r[i][j+1];

for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
        if(i>1&&mm[i][j]!=mm[i-1][j]){
            l[i][j]=max(l[i][j],l[i-1][j]);
            r[i][j]=min(r[i][j],r[i-1][j]);
            up[i][j]=up[i-1][j]+1;
        }

        int x=(r[i][j]-l[i][j]+1);//横向长度
        int xx=min(x,up[i][j]);//竖向长度
        ans=max(ans,x*up[i][j]);//长方形
        an2=max(an2,xx*xx);//正方形

    }
}
printf("%d\n%d",an2,ans);
return 0;

}