P7074做题记录
分析和思路
题面很简单,就是求从左上到右下的最大代价(不允许重复)。但是唯一的一个限制使得这道看似简单的题成为了一个绿题,就是移动只有三个方向:右,上,下。
假如方向少了一个,只有向右和向下,那么这道题就成为了那个经典且简单的dp题,但是此时多了一个方向,使用原先的状态转移方程就失去了无后效性。
于是乎,我就不会用dp写了… 只会用dfs。dfs的思路很简单,傻傻向下搜,记录最大值就行了。如果不加入剪枝,记忆化等优化技巧,综合该题的数据范围。得到预估得分为30。
那么就还需要思考如何去写dp。
首先,我们思考,只能走右不能走左,意味着我们可以单独将每一列分开求解,而向右走其实是从某列运动到下一列,下一列再运动到下下一列……实质上是一个递推的过程。
然后对于每一列,我们会发现要么只能往上走,要么只能往下走。
于是可以考虑对于状态增加一维,记录来时的方向(dp[x][y][k])(k<3) 表示前x行,前y列,对于第k个方向来时取得的最大代价。
再考虑状态转移方程
我们先规定k=0时表示从左边来,k=1时表示从上面来,k=2时表示从下面来
dp[i][j][0]等于什么呢?
由于k=0,所以它的上一步只能从左边来,左边来的总代价可能有三种情况dp[i][j-1][0],dp[i][j-1][1],dp[i][j-1][2],取最大值就好了,最后再加上它这个格子的代价
$$dp[i][j][0]=max(dp[i][j-1][0],dp[i][j-1][1],dp[i][j-1][2])+a[i][j]$$
再考虑dp[i][j][1]的状态转移方程
由于k=1,所以他的上一步只能从上面来,上面来的总代价就只有两种情况了dp[i-1][j][0],dp[i-1][j][1]。由于不能重复走,所以就不可能上面来的格子的更上一步从下面来,因此就不能将dp[i][j][2]考虑进去。所以它的状态转移方程为
$$dp[i][j][1]=max(dp[i-1][j][0],dp[i-1][j][1])+a[i][j]$$
同理可得k=2时的情况
$$dp[i][j][2]=max(dp[i+1][j][0],dp[i+1][j][2])+a[i][j]$$
想到这里,这题就完成大半了,接下来就是要将思路转换成代码。
实现与细节
十年OI一场空,不开long long见祖宗
因此还是直接define int long long吧初始化
dp最难的细节就是初始化
第一列直接初始化dp[i][j][1]枚举顺序
考虑递推的过程是按列的,所以外层循环枚举列
再考虑到k=2时,前行被后行影响,所以要从后往前枚举
我关于这些细节踩过的坑
完整代码
1 |
|
总结与反思
注重dp的特征,思考转移过程