BZOJ 1057 棋盘制作(最大01相间子矩阵)

求最大01相间子矩阵可以转换为求最大全0子矩阵。只需把棋盘(x+y)为奇数的取反,而该问题可以用经典的悬线法O(n^2)的求解。

悬线法呢。

首先定义b[i][j],为a[i][j]向上的最大连续0的长度。可以O(n^2)递推求出。

l[i][j],为b[i][j]向左边的最大连续序列的长度,该连续序列满足b[i][k]<=b[i][j]. r[i][j]同理。

那么以该点a[i][j]拓展的最大全0子矩阵即为 b[i][j]*(l[i][j]+r[i][j]+1).

最大全0子正方形则取两者的min值相乘即可。

 

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-9
# define MOD 12345678
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())=='-') flag=1;
    else if(ch>='0'&&ch<='9') res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')  res=res*10+(ch-'0');
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}
const int N=2005;
//Code begin...

int a[N][N], b[N][N], l[N][N], r[N][N], ans1, ans2, n, m, st[N], head;

void sol()
{
    FOR(i,1,n) FOR(j,1,m) b[i][j]=(a[i][j])?0:b[i-1][j]+1;
    FOR(i,1,n) {
        head=0; st[head]=0;
        FOR(j,1,m) {
            while (head>0&&b[i][st[head]]>=b[i][j]) --head;
            l[i][j]=j-st[head]-1; st[++head]=j;
        }
        head=0; st[head]=m+1;
        for (int j=m; j>=1; --j) {
            while (head>0&&b[i][st[head]]>=b[i][j]) --head;
            r[i][j]=st[head]-j-1; st[++head]=j;
        }
        FOR(j,1,m) {
            ans1=max(ans1,min(b[i][j],l[i][j]+r[i][j]+1)*min(b[i][j],l[i][j]+r[i][j]+1));
            ans2=max(ans2,b[i][j]*(l[i][j]+r[i][j]+1));
        }
    }
}
int main ()
{
    scanf("%d%d",&n,&m);
    FOR(i,1,n) FOR(j,1,m) {
        scanf("%d",&a[i][j]);
        if ((i+j)&1) a[i][j]^=1;
    }
    sol();
    FOR(i,1,n) FOR(j,1,m) a[i][j]^=1;
    sol();
    printf("%d\n%d\n",ans1,ans2);
    return 0;
}
View Code

 

posted @ 2017-03-04 18:38  free-loop  阅读(218)  评论(0编辑  收藏  举报