题目链接:https://www.luogu.org/problemnew/show/P1736
将问题转化一下,其实就是在01矩阵中找边长最大的正方形(对角线长在此等于边长),这和那道最大正方形很像。因为当时最大正方形用的是一种比较落后的思想,所以这道题也没有顺利A掉。其实这道题的思路还是很容易理解的,先讨论左上到右下的对角线,我们定义dp[i][j]表示以(i,j)为右下角的最大正方形的边长,l1(i,j)表示从(i,j)往左最多有几个连续的0,l2(i,j)表示从(i,j)往上最多有几个连续的0,那么当map[i][j]==1时,则有dp[i][j]=min(dp[i-1][j-1],min(l1[i][j-1],l2[i-1][j]))+1。因为要保证这个正方形中只有对角线上是1且全是1。然后再修改一下l1,使其保存从(i,j)往右最多有几个连续的0,再从右上到左下跑一边DP,比较两次的最大值就OK了。因为最大正方形那道题里,最大正方形要求里面全是1,而这里要求对角线全是1,且其他地方不能有1,因此我们就需要预处理一下相对于dp[i-1][j-1]多出来的边框,而不能简单地利用dp[i][j-1]和dp[i-1][j]。
1 #include<cstdio> 2 inline int min(int a,int b) {return a<b?a:b;} 3 inline int max(int a,int b) {return a>b?a:b;} 4 const int mmax=2505; 5 int n,m,map[mmax][mmax],l1[mmax][mmax],l2[mmax][mmax],dp[mmax][mmax],ans; 6 int main() { 7 scanf("%d%d",&n,&m); 8 for(int i=1;i<=n;++i) 9 for(int j=1;j<=m;++j) { 10 scanf("%d",&map[i][j]); 11 if(!map[i][j]) l1[i][j]=l1[i][j-1]+1,l2[i][j]=l2[i-1][j]+1; 12 } 13 for(int i=1;i<=n;++i) 14 for(int j=1;j<=m;++j) 15 if(map[i][j]) { 16 dp[i][j]=min(dp[i-1][j-1],min(l1[i][j-1],l2[i-1][j]))+1; 17 ans=max(ans,dp[i][j]); 18 } 19 for(int i=1;i<=n;++i) 20 for(int j=m;j>=1;--j) if(!map[i][j]) l1[i][j]=l1[i][j+1]+1; 21 for(int i=1;i<=n;++i) 22 for(int j=m;j>=1;--j) 23 if(map[i][j]) { 24 dp[i][j]=min(dp[i-1][j+1],min(l1[i][j+1],l2[i-1][j]))+1; 25 ans=max(ans,dp[i][j]); 26 } 27 printf("%d",ans); 28 return 0; 29 }