ZCO 2009 P1:带特殊移动的网格最大权重路径问题求解咨询
嘿,这个问题确实挺棘手的——允许一次反向移动的话,贪心算法肯定抓不住全局最优,毕竟反向移动的收益得结合前后的路径总和来看,不能只看眼前的局部利益。我来分享几种可行的思路,以及这类带特殊规则的路径问题的通用解法:
这个思路的核心是先预处理出两个关键的DP数组,再枚举所有可能使用特殊移动的位置,计算每种情况的最大路径和,最后取所有情况的最大值。
步骤1:预处理正向和反向DP数组
首先我们需要两个DP数组,分别记录从起点到每个点的最优路径,以及从每个点到终点的最优路径:
正向DP数组 forward
forward[i][j] 表示从左上角 (0,0) 走到 (i,j) 的最大路径和。
- 边界条件:
- 起点:
forward[0][0] = grid[0][0] - 第一行(只能从左往右走):
forward[0][j] = forward[0][j-1] + grid[0][j] - 第一列(只能从上往下走):
forward[i][0] = forward[i-1][0] + grid[i][0]
- 起点:
- 递推公式:对于非边界的点,取从上方或左方过来的最优路径:
forward[i][j] = grid[i][j] + max(forward[i-1][j], forward[i][j-1])
反向DP数组 backward
backward[i][j] 表示从 (i,j) 走到右下角 (n-1,n-1) 的最大路径和。
- 边界条件:
- 终点:
backward[n-1][n-1] = grid[n-1][n-1] - 最后一行(只能从右往左走):
backward[n-1][j] = backward[n-1][j+1] + grid[n-1][j] - 最后一列(只能从下往上走):
backward[i][n-1] = backward[i+1][n-1] + grid[i][n-1]
- 终点:
- 递推公式:对于非边界的点,取从下方或右方过去的最优路径:
backward[i][j] = grid[i][j] + max(backward[i+1][j], backward[i][j+1])
步骤2:枚举所有可能的特殊移动场景
现在我们要遍历所有可能使用一次反向移动的情况,计算每种情况的路径和,再和“不使用特殊移动”的结果(也就是 forward[n-1][n-1])比较,取最大值:
场景1:使用一次向左移动
遍历所有 i(0 ≤ i < n)和 j(0 ≤ j < n-1),计算路径:起点→(i,j)→(i,j+1)→(i,j)→终点的总和,公式为:
forward[i][j] + grid[i][j+1] + backward[i][j]
这里的逻辑是:forward[i][j] 是到 (i,j) 的最优和,加上走到 (i,j+1) 的数值,再加上从 (i,j) 到终点的最优和(因为我们折返回到了 (i,j))。
场景2:使用一次向上移动
遍历所有 i(0 ≤ i < n-1)和 j(0 ≤ j < n),计算路径:起点→(i,j)→(i+1,j)→(i,j)→终点的总和,公式为:
forward[i][j] + grid[i+1][j] + backward[i][j]
逻辑和向左移动类似,只是折返方向变成了上下。
步骤3:取所有情况的最大值
最终答案就是以下几个值中的最大值:
- 不使用特殊移动的路径和:
forward[n-1][n-1] - 所有向左移动场景的最大路径和
- 所有向上移动场景的最大路径和
贪心算法失效的原因是,反向移动的收益不是孤立的——你折返拿到的数值,得加上后续路径的最优解才是真正的收益。而双向DP正好把“起点到每个点的最优”和“每个点到终点的最优”都提前算好了,枚举所有可能的折返点,就能覆盖所有使用特殊移动的路径情况。
这种思路还能扩展到类似问题,比如允许k次特殊移动、特殊移动有不同规则等,核心都是预处理双向最优路径,再枚举特殊操作的位置。
比如下面这个网格:
-5 10 20 -1
常规路径的最大和是 -5→20→-1,总和是14。但如果我们使用一次向左移动:走到 (0,1)(数值10),再折返回到 (0,0),然后走 (0,0)→(1,0)→(1,1),总和是 -5+10+(-5)+20+(-1)=19,比常规路径更优。
用我们的方法计算:
forward[0][0] = -5grid[0][1] =10backward[0][0] = max(-5+20+(-1), -5+10+(-1))=14- 计算得
-5+10+14=19,正好是最优结果。
- 时间复杂度:O(n²),正向DP、反向DP以及枚举场景都是O(n²)的操作,整体是线性的平方级复杂度,对于正方形网格来说效率很高。
- 空间复杂度:O(n²),用来存储两个DP数组。如果想要优化空间,可以用一维数组代替二维数组,不过n²的空间通常是可接受的。
还要注意:如果折返的点是负数,那使用特殊移动可能反而不如不使用,所以最后一定要和常规路径的结果比较取最大值,不能漏掉这种情况。
内容的提问来源于stack exchange,提问作者Vasu090

