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; }