题解 P1681 【最大正方形II】

Apro1066

2019-08-21 22:44:38

Solution

其实是最大正方形的变形。 正方形那题是令$dp[i][j]$是以$(i,j)$为右下角能得到的最大正方形边长,而这次只要加一个维度判断黑白格就行了。。 令$dp[i][j][0/1]$是以$(i,j)$为右下角,且该点上为$0/1$时能得到的最大正方形边长,则: **1.当$a[i][j]=0$时** $$dp[i][j][0]=min(dp[i-1][j][1],dp[i][j-1][1],dp[i-1][j-1][0])+1$$ **2.当a[i][j]=1时** $$dp[i][j][1]=min(dp[i-1][j][0],dp[i][j-1][0],dp[i-1][j-1][1])+1$$ 这里来着重讲一下为什么状态转移方程中为什么要取$min$而不是取$max$。 你想啊,点$(i,j)$可以从$(i-1,j)$转移过来,也可以从$(i,j-1)$转移过来,也可以从$(i-1,j-1)$转移过来。这$3$个点要同时兼顾,而$(i,j)$是决定存在性问题的,如果我取$max$,我这边可能满足条件,但就不保证其他两边满足条件了。如果你不能理解,我们举个例子就明白了: 假如有一个a数组$a[4][4]$: ``` 1 0 1 0 0 1 0 1 1 0 1 0 0 1 0 0 ``` 则dp数组:(这里$dp$数组用$2$维表示,为了举例方便) 1 ``` 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ``` 2 ``` 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 ``` 在$dp[2][2]$中,显然可以从$dp[1][2]$,$dp[2][1]$,$dp[1][1]$这$3$个点转移,看一下a数组: ``` 1 0 0 1 ``` 是可以转移的,于是dp数组 ``` 1 1 1 2 ``` dp数组 3 ``` 1 1 1 1 1 2 2 2 1 2 2 2 1 2 2 0 ``` 等等,$dp[3][3]$应该为$3$啊! 好。 ``` 1 1 1 1 1 2 2 2 1 2 3 2 1 2 2 0 ``` 那么我们接着看$dp[4][3]$,发现$dp[4][2]=2$,$dp[3][3]=3$,$dp[3][2]=2$,周围有2个2,1个3,选哪个呢? 好,按照我们之前的惯性思维,如果取$max$的话,也就是选$dp[3][3]$的话,则dp数组: ``` 1 1 1 1 1 2 2 2 1 2 3 2 1 2 4 0 ``` 发现它并不能构成边长为$4$的正方形!所以要选边长为$2$的。 最后dp数组: ``` 1 1 1 1 1 2 2 2 1 2 3 3 1 2 3 0 ``` 也就是说,$dp[i][j]$的选择,是要兼顾$dp[i-1][j]$,$dp[i][j-1]$,$dp[i-1][j-1]$的最小值的。如果取最大值的话,显然不能保证$dp[i][j]$是否还能正确。取最小值是为了能保证$dp[i][j]$也能满足条件,随着$i$和$j$的增大,$dp[i][j]$一定能保证单调递增的。 ```cpp #include <stdio.h> #include <iostream> #define inf 2e9+7 using namespace std; int dp[1501][1501][2],a[1501][1501],n,m,s; signed main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); register int i,j; cin>>n>>m; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { cin>>a[i][j]; } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(a[i][j]==0) { dp[i][j][0]=min(min(dp[i-1][j][1],dp[i][j-1][1]),dp[i-1][j-1][0])+1; s=max(s,dp[i][j][0]); } if(a[i][j]==1) { dp[i][j][1]=min(min(dp[i-1][j][0],dp[i][j-1][0]),dp[i-1][j-1][1])+1; s=max(s,dp[i][j][1]); } } } cout<<s<<endl; return 0; } ```