LC 741. 摘樱桃 (opens new window) (opens new window)
困难
# 问题描述
一个 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。
# 记忆化搜索
可以将题意等价转换为两个人从 开始走,同时走到 时能摘到的最大值,从 开始走,枚举两个人同时走的方案,若走出边界或者遇到荆棘,则返回负无穷,若两个人走到两个不同的点,则将两个点的樱桃采摘,若走到相同的点,则其中一人采摘,最终采摘方案最大的即为结果。 因为从地图中某个点出发到 的最大值是固定的,所以,可以将结果缓存起来,后续方案若遇到相同的点可以直接返回。
/**
* @param {number[][]} grid
* @return {number}
*/
var cherryPickup = function (grid) {
const n = grid.length
const meno = new Map()
const dfs = (x1, y1, x2, y2) => {
if (x1 >= n || y1 >= n || x2 >= n || y2 >= n) {
return -Infinity
}
if (grid[x1][y1] === -1 || grid[x2][y2] === -1) {
return -Infinity
}
if (meno.has(`${x1}-${y1}-${x2}-${y2}`)) {
return meno.get(`${x1}-${y1}-${x2}-${y2}`)
}
let cnt = 0
if (x1 !== x2 && y1 !== y2) {
cnt += grid[x1][y1] + grid[x2][y2]
} else {
cnt += grid[x1][y1]
}
if (x1 === n - 1 && y1 === n - 1) {
return cnt
}
cnt += Math.max(
dfs(x1 + 1, y1, x2 + 1, y2),
dfs(x1, y1 + 1, x2, y2 + 1),
dfs(x1 + 1, y1, x2, y2 + 1),
dfs(x1, y1 + 1, x2 + 1, y2)
)
meno.set(`${x1}-${y1}-${x2}-${y2}`, cnt)
return cnt
}
return Math.max(dfs(0, 0, 0, 0), 0)
}
- 时间复杂度:
- 空间复杂度:
# 动态规划
假设两人坐标为 ,,当前走的步数为 ( 的取值范围是 ),对应每个 必然有 ,因此可以定义 为走了 步时,当前两人的坐标为 和 的最大樱桃采摘数量。对于第 步,可以由第 步转移,因此有状态转移方程:
表示意义为:
- 表示向右走一步,向右走一步
- 表示向下走一步,向右走一步
- 表示向右走一步,向下走一步
- 表示向下走一步,向下走一步
走到下一个坐标后,若 和 位置都不为荆棘,则可以采摘, 采摘当前坐标樱桃,若 与 坐标不同,则 也采摘当前坐标樱桃。
/**
* @param {number[][]} grid
* @return {number}
*/
var cherryPickup = function (grid) {
const n = grid.length
const dp = new Array(2 * n - 1).fill(0).map(() => new Array(n).fill(0).map(() => new Array(n).fill(-Infinity)))
dp[0][0][0] = grid[0][0]
for (let k = 1; k < 2 * n - 1; k++) {
for (let x1 = 0; x1 <= Math.min(k, n - 1); x1++) {
for (let x2 = 0; x2 <= Math.min(k, n - 1); x2++) {
let y1 = k - x1
let y2 = k - x2
if (grid[x1][y1] === -1 || grid[x2][y2] === -1) continue
let cnt = dp[k - 1][x1][x2]
if (x1 > 0) cnt = Math.max(cnt, dp[k - 1][x1 - 1][x2])
if (x2 > 0) cnt = Math.max(cnt, dp[k - 1][x1][x2 - 1])
if (x1 > 0 && x2 > 0) cnt = Math.max(cnt, dp[k - 1][x1 - 1][x2 - 1])
cnt += grid[x1][y1]
if (x1 !== x2) cnt += grid[x2][y2]
dp[k][x1][x2] = cnt
}
}
}
return Math.max(dp[2 * n - 2][n - 1][n - 1], 0)
}
- 时间复杂度:
- 空间复杂度:
上次更新: 2023/01/31 19:48:05
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 , 转载请注明出处!