bzoj1057: [ZJOI2007]棋盘制作

dp。方型棋盘不用说,矩形棋盘每个点先维护先上能达到的最大距离v。然后dp找出以自己的v最小时向左向右能达到最大的距离l,r。

因为最大矩形棋盘的宽度肯定等于某个点的v,我们又求出了每个v对应的最长距离(r-l+1),所以正确性得以保证。

还有一个小技巧,把横纵坐标和为奇数的点异或。就可以把原问题转化为求最大的01矩阵了。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 2000 + 10;
int a[maxn][maxn],f[maxn][maxn],v[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
int n,m,res1=0,res2=0;

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]);
        if((i+j)%2) a[i][j]^=1;
        //printf("a[%d][%d] = %d\n",i,j,a[i][j]);
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) {
        if(i==1&&j==1) f[i][j]=1;
        else if(a[i][j]!=a[i][j-1] || a[i][j] != a[i-1][j]) f[i][j]=1;
        else f[i][j] = min(f[i-1][j-1],min(f[i-1][j],f[i][j-1]))+1;
        res1=max(res1,f[i][j]);
        //printf("f[%d][%d] = %d\n",i,j,f[i][j]);
    }
    printf("%d\n",res1*res1);
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) {
        if(i==1) v[i][j]=1;
        else if(a[i-1][j]!=a[i][j]) v[i][j]=1;
        else v[i][j]=v[i-1][j]+1;
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) {
        if(j==1) l[i][j]=1;
        else if(v[i][j]>v[i][j-1] || a[i][j]!=a[i][j-1]) l[i][j]=j;
        else {
            l[i][j]=l[i][j-1];
            while(l[i][j]>1 && v[i][j]<=v[i][l[i][j]-1] && a[i][l[i][j]-1]==a[i][j]) l[i][j]=l[i][l[i][j]-1];
        }
    }
    for(int i=1;i<=n;i++)
    for(int j=m;j>=1;j--) {
        if(j==m) r[i][j]=m;
        else if(v[i][j]>v[i][j+1] || a[i][j] != a[i][j+1]) r[i][j]=j;
        else {
            r[i][j] = r[i][j+1];
            while(r[i][j]<n && v[i][j]<=v[i][r[i][j]+1] && a[i][r[i][j]+1]==a[i][j]) r[i][j]=r[i][r[i][j]+1];
        }
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++) 
        res2=max(res2,v[i][j]*(r[i][j]-l[i][j]+1));
    printf("%d\n",res2);
    return 0;
}
posted @ 2016-04-24 09:54  invoid  阅读(...)  评论(... 编辑 收藏