求最大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 %d ",ans1,ans2); return 0; }