[BZOJ1057][ZJOI2007]棋盘制作(悬线法)

题目描述

传送门

题解

设h(i,j)表示以(i,j)为下端点的悬线的最长长度,l(i,j)和r(i,j)分别表示使悬线有此长度的左边最近的限制和右边最近的限制。
预处理L(i,j)R(i,j)分别表示点(i,j)能扩展到的最近的不合法点。
如果(i,j)与(i-1,j)颜色相同,那么h(i,j)=1,l(i,j)=L(i,j),r(i,j)=R(i,,j);
如果(i,j)与(i-1,j)颜色不相同,那么h(i,j)=h(i-1,j)+1,l(i,j)=max(l(i-1,j),L(i,j)),r(i,j)=min(r(i-1,j),R(i,j)).
矩形的答案即为max(h(i,j)*(r(ii,j)-l(i,j)-1)),正方形需要把长宽取一下min

代码

#include
#include
#include
using namespace std;
#define N 2005

int n,m,ans1,ans2;
int a[N][N],L[N][N],R[N][N],l[N][N],r[N][N],h[N][N];

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
            scanf("%d",&a[i][j]);
    for (int i=1;i<=n;++i)
    {
        L[i][1]=0;
        for (int j=2;j<=m;++j)
            if (a[i][j]!=a[i][j-1]) L[i][j]=L[i][j-1];
            else L[i][j]=j-1;
        R[i][m]=m+1;
        for (int j=m-1;j>=1;--j)
            if (a[i][j]!=a[i][j+1]) R[i][j]=R[i][j+1];
            else R[i][j]=j+1;
    }
    for (int i=1;i<=m;++i) h[1][i]=1,l[1][i]=L[1][i],r[1][i]=R[1][i];
    for (int i=2;i<=n;++i)
    {
        for (int j=1;j<=m;++j)
            if (a[i][j]==a[i-1][j])
            {
                h[i][j]=1;
                l[i][j]=L[i][j],r[i][j]=R[i][j];
            }
            else
            {
                h[i][j]=h[i-1][j]+1;
                l[i][j]=max(l[i-1][j],L[i][j]);
                r[i][j]=min(r[i-1][j],R[i][j]);
            }
    }
    for (int i=1;i<=n;++i)
        for (int j=1;j<=m;++j)
        {
            int len=min(h[i][j],r[i][j]-l[i][j]-1);
            if (len>0) ans1=max(ans1,len*len);
            ans2=max(ans2,h[i][j]*(r[i][j]-l[i][j]-1));
        }
    printf("%d\n%d\n",ans1,ans2);
}


你可能感兴趣的:(题解,悬线法)