P7365 题解

· · 题解

描述

这道题是一个较为细节的 dp 题目。

细节部分在于它足足有四个转移方程所以及其细节。

洛谷博客版。

题意

在给定网格 0 1 中找出最大的 I 形的 0 部分,输出最大网格区块。

先前定义

我们需要用两类数组来方便转移。

注:为什么要利用新数组来辅助计算呢,因为在运行过程中,我们需要不断枚举每个 l,r 区间的最大值来进行转移方程,若如此,复杂度将会达到惊人的 O(n^4) ,但实际上这里的计算与 dp 的计算是独立的,当前互相不干扰的,所以可以提前储存,来降低复杂度。

理解

由于 I 的独特性质(上面部分大于中间部分,中间部分小于下面部分)所以我们需要去分类三种情况去讨论各自的转移方程。

利用图片与代码来方便理解。

不知道各位能不能理解。

由于 P1 部分大于 P2 部分,所以我们需要将 L-1R+1f 部分转移到 dp_{L,R} 中 因而有方程

同理 由于 $P2$ 部分小于 $P3$ 部分,所以我们的 $dp_{L,R}$ 的部分需要由 $L+1$ 和 $R-1$ 的 $f$ 部分中转移得到 因而有方程 $dp_{id,1,r,3}=\max(Dp_{id,1,r,3},f_{l+1,r-1,2}+len)$。 最后对 $dp_{id,l,r,3}$ 取一个最大值即为答案。 自此 所有的转移方程部分已经结束,下面便是代码。 ## Std ```cpp #include<bits/stdc++.h> using namespace std; const int maxn=2e2+5; int n,m,id,x,ans; int dp[2][maxn][maxn][4]; int sum[maxn]; int f[maxn][maxn][3];//用来辅助计算 p2 跟 p3 的最值。 signed main(){ ios::sync_with_stdio(false); cin.tie(nullptr),cout.tie(nullptr); cin>>n>>m; memset(f,-0x3f3f3f,sizeof(f)); memset(dp,-0x3f3f3f,sizeof(dp)); for(int i=1,x;i<=n;i++){ for(int j=1,x;j<=m;j++)cin>>x,sum[j]=sum[j-1]+x;//维护前缀和。 memset(dp[id],-0x3f3f3f,sizeof(dp[id]));//在“建立”完新数组之后 初始化。 for(int p=3;p>=1;p--){ for(int l=1;l<=m;l++){//遍历左区间 for(int r=l;r<=m;r++){//遍历右区间 if(sum[r]-sum[l-1]==0){ int len=r-l+1; if(p==1)dp[id^1][l][r][1]=max(dp[id^1][l][r][1],0);//初始化数组 在跑 p1 时 初始化为 0 或保留原值。 dp[id][l][r][p]=dp[id^1][l][r][p]+len;//如果先前数组有大于 0 的值,则继承并加上长度,否则不变。 if(p==2)dp[id][l][r][2]=max(dp[id][l][r][2],f[l-1][r+1][1]+len);//方程 3 得。 if(p==3)dp[id][l][r][3]=max(dp[id][l][r][3],f[l+1][r-1][2]+len);//方程 4 得。 } } } if(p==1) for(int l=1;l<=m;l++) for(int r=m;r>=l;r--) f[l][r][1]=max(dp[id][l][r][1], max(f[l-1][r][1],f[l][r+1][1]));//方程 1 得。 if(p==2) for(int l=m;l>=1;l--) for(int r=l;r<=m;r++) f[l][r][2]=max(dp[id][l][r][2], max(f[l+1][r][2],f[l][r-1][2]));//方程 2 得。 if(p==3) for(int l=1;l<=m;l++) for(int r=1;r<=m;r++) ans=max(ans,dp[id][l][r][3]);//取最大值。 } id^=1;//转换到新数组上。 } cout<<ans; return 0; } ``` 祝大家早日 AC。 ##### 实在理解不了的话,记得手跑代码试试哦。