luogu p1387 最大正方形

原题链接:https://www.luogu.org/problem/show?pid=1387

 

理论上来说可以暴力枚举边长和右下角坐标,而且似乎有人能用这方法过这道题。。。

不过枚举太不现实,而且数据强一点的话就彻底没办法了,还是要DP。

用两个数组l,w分别存储每个位置(包括本身)向左和向上各有几个连续的1,如果一个位置是1,这个位置的w和l就能从别的位置转移过来。(似乎预处理能与输入一起完成?不过我没这么做。)

然后对f数组进行处理,对于每一个正方形,我们都可以把它看作是一层一层的,像这样:

第一层:

1

第二层:

0 1

1 1

第三层:

0 0 1

0 0 1

1 1 1

如果能构成正方形,那么前面的几层都必须是完整的。对于每一个a[i][j]==1的位置,f[i][j]的取值为f[i-1][j-1]+1,l[i][j]与w[i][j]的最小值

插一句,luogu上数据略水,处理f的时候把m写成n居然都有90分。。。

 

#include<cstdio>
void read(int &y)
{
    y=0;char x=getchar();
    while(x<'0'||x>'9') x=getchar();
    while(x>='0'&&x<='9')
    {
        y=y*10+x-'0';
        x=getchar();
    }
}
int n,m,a[105][105],ans;
int f[105][105],l[105][105],w[105][105];
int min(int x,int y)
{
    if(x<y) return x;
    else return y;
}
int main()
{
    read(n);read(m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++) read(a[i][j]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i][j]==1)
            {
                w[i][j]=w[i-1][j]+1;
                l[i][j]=l[i][j-1]+1;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i][j]==1)
            {
                f[i][j]=min(min(w[i][j],l[i][j]),f[i-1][j-1]+1);
                if(ans<f[i][j]) ans=f[i][j];
            }
        }
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2017-09-21 07:13  Excim  阅读(150)  评论(0编辑  收藏  举报