1. 问题描述:
一个N x N的网格(grid) 代表了一块樱桃地,每个格子由以下三种数字的一种来表示:
0 表示这个格子是空的,所以你可以穿过它。1 表示这个格子里装着一个樱桃,你可以摘到樱桃然后穿过它。-1 表示这个格子里有荆棘,挡着你的路。你的任务是在遵守下列规则的情况下,尽可能的摘到最多樱桃:从位置 (0, 0) 出发,最后到达 (N - 1,N - 1) ,只能向下或向右走,并且只能穿越有效的格子(即只可以穿过值为0或者1的格子);当到达 (N - 1,,N - 1) 后,你要继续走,直到返回到 (0, 0) ,只能向上或向左走,并且只能穿越有效的格子;当你经过一个格子且这个格子包含一个樱桃时,你将摘到樱桃并且这个格子会变成空的(值变为0);如果在 (0,0) 和 (N - 1, N - 1) 之间不存在一条可经过的路径,则没有任何一个樱桃能被摘到。
示例 1:
输入: grid =
[[0, 1, -1],
[1, 0, -1],
[1, 1, 1]]
输出: 5
解释:
玩家从(0,0)点出发,经过了向下走,向下走,向右走,向右走,到达了点(2, 2)。
在这趟单程中,总共摘到了4颗樱桃,矩阵变成了[[0,1,-1],[0,0,-1],[0,0,0]]。
接着,这名玩家向左走,向上走,向上走,向左走,返回了起始点,又摘到了1颗樱桃。
在旅程中,总共摘到了5颗樱桃,这是可以摘到的最大值了。
说明:
grid 是一个 N * N 的二维数组,N的取值范围是1 <= N <= 50。
每一个 grid[i][j] 都是集合 {-1, 0, 1}其中的一个数。
可以保证起点 grid[0][0] 和终点 grid[N - 1][N - 1] 的值都不会是 -1。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cherry-pickup
2. 思路分析:
分析题目可以知道我们一开始的时候需要从起点到达终点,然后又从终点返回起点,计算这两条路径能够获得的最大樱桃数目,这两个操作等价于从起点开始同时走两条路径最终到达终点的操作。对于从起点到终点走一遍的路径可以枚举横坐标与纵坐标使用动态规划的思想解决即可,由于这道题目需要走两遍路径所以涉及到两个坐标,所以只枚举一条路线的横纵坐标是不够的,怎么样确定两条线路的坐标呢?其实我们在枚举的时候可以增加多一个枚举的变量k,其中k为路线的横坐标与纵坐标的和,我们可以定义一个三维数组或者三维列表dp,其中dp[i][j][k]表示第一条路线走到当前位置的横坐标为i,第二条路线走到当前位置的横坐标为j,路线的横坐标与纵坐标之和为k,这样我们就可以使用三层循环枚举所有的状态,最外层循环枚举当前的k,其余两层循环枚举表示第一条路线当前位置与第二条路线当前位置的横坐标,通过三层循环我们就可以确定两条路线具体的两个坐标分别为(i,k - i)和(j,k - j),怎么样进行状态的计算呢?如果只有一条路线那么上一个状态到达当前状态总共有两种走法,分别是往右走与往下走,因为是两条路线所以组合之后存在四种走法,分别对应四个状态我们只需要枚举这四种状态然后就可以得到当前状态的dp值(当前状态dp[i][j][k]对应着四种走法):
3. 代码如下:
from typing import List
class Solution:
def cherryPickup(self, grid: List[List[int]]) -> int:
n = len(grid)
dp = [[[-10 ** 9] * (2 * n + 1) for i in range(n + 1)] for j in range(n + 1)]
# 从第一个位置开始dp, 所以第一个位置应该初始化为grid[0][0]
if grid[0][0] != -1: dp[1][1][2] = grid[0][0]
# k从3开始表示从第一个位置的右边或者下边开始递推
for k in range(3, 2 * n + 1):
# 第一条路径到达当前位置的横坐标
for i in range(max(1, k - n), min(n, k - 1) + 1):
# 第二条路径到达当前位置的横坐标
for j in range(max(1, k - n), min((n, k - 1)) + 1):
# 当前位置为-1是不合法的那么直接跳过
if grid[i - 1][k - 1 - i] == -1 or grid[j - 1][k - 1 - j] == -1: continue
t = grid[i - 1][k - i - 1]
# 两条路径走的不是同一个位置那么加上对应位置的值
if i != j: t += grid[j - 1][k - j - 1]
# 枚举两条路径的上一个状态, 总共有四种走法走到当前的状态
for a in range(i - 1, i + 1):
for b in range(j - 1, j + 1):
dp[i][j][k] = max(dp[i][j][k], t + dp[a][b][k - 1])
# 可以不走或者是两条路径都走到了最后一个位置
return max(0, dp[n][n][2 * n])