[ZJOI2007]棋盘制作

做题日期:2020.12.01

\(【题目描述】\)

有一个\(N\cdot M(N,M \leq 10^3)\)\(01\)棋盘,问棋盘上最大的01交错(即相邻两个数不相同)的正方形和矩形的大小分别是多少。

\(【输入样例】\)

3 3
1 0 1
0 1 0
1 0 0

\(【输出样例】\)

4
6

\(【考点】\)

动态规划、悬线法

\(【做法】\)

悬线法模板。定义\(left[i][j]\)表示\((i,j)\)到最左能到达的点的纵坐标,\(right[i][j]\)表示\((i,j)\)到最右能到达的点的纵坐标,\(up[i][j]\)表示\((i,j)\)到最上能到达的点与\((i,j)\)之间的距离。

因为矩阵要求数字互不相同,因此在\(a[i][j]!=a[i-1][j]\)\(a[i][j]!=a[i][j-1]\)\(a[i][j]!=a[i][j+1]\)时转移即可。

处理正方形,只需要在答案\(ans=\max(ans,(right[i][j]-left[i][j]+1)\cdot up[i][j])\)转移的时候,获取当前最大矩阵的两条边中较小的那一条边,即为当前最大正方形的变成,使用其平方转移即可。

\(【代码】\)

#include<cstdio>
#include<iomanip>
using namespace std;
const int N=2e3+50;
int a[N][N];
int lft[N][N],rgt[N][N],up[N][N];
int n,m,ans1,ans2;

inline int Min(int a,int b){return a<b?a:b;}
inline 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",&a[i][j]);
            lft[i][j]=rgt[i][j]=j;//初始化
            up[i][j]=1;
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=2;j<=m;j++){
            if(a[i][j]!=a[i][j-1]) lft[i][j]=lft[i][j-1];//左右转移
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=m-1;j>=1;j--){
            if(a[i][j]!=a[i][j+1]) rgt[i][j]=rgt[i][j+1];//左右转移
        }
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(i>1&&a[i][j]!=a[i-1][j]){
                rgt[i][j]=Min(rgt[i-1][j],rgt[i][j]);
                lft[i][j]=Max(lft[i-1][j],lft[i][j]);//上下转移
                up[i][j]=up[i-1][j]+1;
            }
            int mine=Min(up[i][j],rgt[i][j]-lft[i][j]+1);
            ans1=Max(ans1,mine*mine);
            ans2=Max(ans2,(rgt[i][j]-lft[i][j]+1)*up[i][j]);
        }
    }
    
    printf("%d\n%d\n",ans1,ans2);
    return 0;
}
posted @ 2020-12-01 20:22  lxzy  阅读(85)  评论(0)    收藏  举报