【洛谷习题】创意吃鱼法

题目链接: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 }
AC代码

猜你喜欢

转载自www.cnblogs.com/Mr94Kevin/p/9625367.html