[BZOJ 1057] 棋盘制作

Link:

BZOJ 1057 传送门

Solution:

一道求最大子矩阵的裸题

一般求最大子矩阵的方法为单调栈(BZOJ 3039 玉蟾宫 )/ 垂线法

 

两者的思想其实完全相同,不过以前没写过垂线法,这次来练一练

垂线法要记录3个状态:

$h[i][j]$记录当前点能向上合法走的步数

$l[i][j]$记录高为$h[i][j]$的线最多能左移到哪里,同理$r[i][j]$表示能右移到何处

 

我们先算出仅在每一行中$r、l$的值,如果向上合法则用:

$l[i][j]=max(l[i][j],l[i-1][j])$和$r[i][j]=min(r[i][j],r[i-1][j])$来递推即可

最终用$(r[i][j]-l[i][j]+1)*h[i][j]$来更新答案

Code:

#include <bits/stdc++.h>

using namespace std;
const int MAXN=2005;
int res1=0,res2=0;
int n,m,dat[MAXN][MAXN],l[MAXN][MAXN],r[MAXN][MAXN],h[MAXN][MAXN];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&dat[i][j]);
            if((i+j)&1) dat[i][j]^=1;
            l[i][j]=r[i][j]=j;
            h[i][j]=(dat[i][j]==dat[i-1][j])?h[i-1][j]+1:1;
        }
    for(int i=1;i<=n;i++)
    {
        for(int j=2;j<=m;j++)
            if(dat[i][j]==dat[i][j-1]) l[i][j]=l[i][j-1];
        for(int j=m-1;j;j--)
            if(dat[i][j]==dat[i][j+1]) r[i][j]=r[i][j+1];
    }
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            if(i>1&&dat[i-1][j]==dat[i][j])
                l[i][j]=max(l[i][j],l[i-1][j]),r[i][j]=min(r[i][j],r[i-1][j]);
            int len=r[i][j]-l[i][j]+1;
            res1=max(res1,len*h[i][j]);
            int sqr=min(len,h[i][j]);
            res2=max(res2,sqr*sqr);
        }
    printf("%d\n%d",res2,res1);
    return 0;
}

 

Review:

1、关于黑白染色

由于此题要求是0/1相间才为合法,可以使用一个很妙的$Trick$:

将$i+j$为奇数/偶数的位置0/1调换,即可转化为原基本模型(0/1相同才合法)

 

有关两种状态染色的题目考虑一下$i+j$奇偶性相同的位置!

(进行交换/翻转等操作,或利用其推导性质)

 

2、结论:最大正方形一定在最大矩形中

有关正方形的题目可以转化为求解矩形

 

3、垂线法中$l,r$都一定要初始化为$j$

 

posted @ 2018-07-03 19:50  NewErA  阅读(...)  评论(... 编辑 收藏