luogu p1387 最大正方形
原题链接:https://www.luogu.org/problem/show?pid=1387
理论上来说可以暴力枚举边长和右下角坐标,而且似乎有人能用这方法过这道题。。。
不过枚举太不现实,而且数据强一点的话就彻底没办法了,还是要DP。
用两个数组l,w分别存储每个位置(包括本身)向左和向上各有几个连续的1,如果一个位置是1,这个位置的w和l就能从别的位置转移过来。(似乎预处理能与输入一起完成?不过我没这么做。)
然后对f数组进行处理,对于每一个正方形,我们都可以把它看作是一层一层的,像这样:
第一层:
1
第二层:
0 1
1 1
第三层:
0 0 1
0 0 1
1 1 1
如果能构成正方形,那么前面的几层都必须是完整的。对于每一个a[i][j]==1的位置,f[i][j]的取值为f[i-1][j-1]+1,l[i][j]与w[i][j]的最小值
插一句,luogu上数据略水,处理f的时候把m写成n居然都有90分。。。
#include<cstdio> void read(int &y) { y=0;char x=getchar(); while(x<'0'||x>'9') x=getchar(); while(x>='0'&&x<='9') { y=y*10+x-'0'; x=getchar(); } } int n,m,a[105][105],ans; int f[105][105],l[105][105],w[105][105]; int min(int x,int y) { if(x<y) return x; else return y; } int main() { read(n);read(m); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) read(a[i][j]); } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(a[i][j]==1) { w[i][j]=w[i-1][j]+1; l[i][j]=l[i][j-1]+1; } } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(a[i][j]==1) { f[i][j]=min(min(w[i][j],l[i][j]),f[i-1][j-1]+1); if(ans<f[i][j]) ans=f[i][j]; } } } printf("%d",ans); return 0; }