[BZOJ 1057] 棋盘制作
Link:
Solution:
一道求最大子矩阵的裸题
一般求最大子矩阵的方法为单调栈(BZOJ 3039 玉蟾宫 )/ 垂线法
两者的思想其实完全相同,不过以前没写过垂线法,这次来练一练
垂线法要记录3个状态:
$h[i][j]$记录当前点能向上合法走的步数
$l[i][j]$记录高为$h[i][j]$的线最多能左移到哪里,同理$r[i][j]$表示能右移到何处
我们先算出仅在每一行中$r、l$的值,如果向上合法则用:
$l[i][j]=max(l[i][j],l[i-1][j])$和$r[i][j]=min(r[i][j],r[i-1][j])$来递推即可
最终用$(r[i][j]-l[i][j]+1)*h[i][j]$来更新答案
Code:
#include <bits/stdc++.h> using namespace std; const int MAXN=2005; int res1=0,res2=0; int n,m,dat[MAXN][MAXN],l[MAXN][MAXN],r[MAXN][MAXN],h[MAXN][MAXN]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&dat[i][j]); if((i+j)&1) dat[i][j]^=1; l[i][j]=r[i][j]=j; h[i][j]=(dat[i][j]==dat[i-1][j])?h[i-1][j]+1:1; } for(int i=1;i<=n;i++) { for(int j=2;j<=m;j++) if(dat[i][j]==dat[i][j-1]) l[i][j]=l[i][j-1]; for(int j=m-1;j;j--) if(dat[i][j]==dat[i][j+1]) r[i][j]=r[i][j+1]; } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(i>1&&dat[i-1][j]==dat[i][j]) l[i][j]=max(l[i][j],l[i-1][j]),r[i][j]=min(r[i][j],r[i-1][j]); int len=r[i][j]-l[i][j]+1; res1=max(res1,len*h[i][j]); int sqr=min(len,h[i][j]); res2=max(res2,sqr*sqr); } printf("%d\n%d",res2,res1); return 0; }
Review:
1、关于黑白染色
由于此题要求是0/1相间才为合法,可以使用一个很妙的$Trick$:
将$i+j$为奇数/偶数的位置0/1调换,即可转化为原基本模型(0/1相同才合法)
有关两种状态染色的题目考虑一下$i+j$奇偶性相同的位置!
(进行交换/翻转等操作,或利用其推导性质)
2、结论:最大正方形一定在最大矩形中
有关正方形的题目可以转化为求解矩形
3、垂线法中$l,r$都一定要初始化为$j$