在问 · 算法题解

741. 摘樱桃

题目描述

输入 n x n 网格 grid。1 表示格子里有一颗樱桃,0 表示空地,-1 表示荆棘,荆棘格不能进入。

先从左上角 (0, 0) 出发,每步只能向右或向下,走到右下角 (n - 1, n - 1);再从右下角出发,每步只能向左或向上,走回 (0, 0)。途中只能经过值为 0 或 1 的格子。

经过有樱桃的格子就摘下它,之后该格变为空地,所以同一颗樱桃只算一次。

返回往返一趟最多能摘到的樱桃数。如果左上角和右下角之间不存在可走的路径,返回 0。

示例 1
输入:grid = [[0,1,-1],[1,0,-1],[1,1,1]]
输出:5

解释:去程向下、向下、向右、向右摘到 4 颗,回程经过 (0,1) 再摘 1 颗,共 5 颗。

示例 2
输入:grid = [[1,1,-1],[1,-1,1],[-1,1,1]]
输出:0

解释:荆棘挡住了所有通往右下角的路线,只能返回 0。

数据范围

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 50
  • grid[i][j] 只取 -1、0、1。
  • grid[0][0] != -1
  • grid[n - 1][n - 1] != -1

解题思路

回程反过来看也是一条从 (0,0) 向右/向下的路线,所以问题等价于两个人 A、B 同时从起点出发。两人都走了 k 步时一定在同一条反对角线 x+y=k 上,只要记录两人的行号 x1、x2 就能确定位置。dp[x1][x2] 表示走 k 步后两人在这两个位置时最多摘到的樱桃数,由上一步四种移动组合转移而来;两人站在同一格时樱桃只算一次。

为什么正确

处理第 k 层时,dp 中保存的是两人恰好走了 k-1 步时所有状态的最优值,-1 表示不可达;对称性让我们只需计算 x1 <= x2。

复杂度分析

时间 O(n^3),空间 O(n^2)(按层滚动)。

易错点

先贪心走一条最多樱桃的路,再在剩下的格子里走第二条,不一定最优;必须让两条路线同时决策。另外两人站在同一格时樱桃只能算一次。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def cherryPickup(self, grid: List[List[int]]) -> int:
        n = len(grid)
        dp = [[-1] * n for _ in range(n)]
        dp[0][0] = grid[0][0]
        for k in range(1, 2 * n - 1):
            nd = [[-1] * n for _ in range(n)]
            for x1 in range(max(0, k - n + 1), min(k, n - 1) + 1):
                for x2 in range(x1, min(k, n - 1) + 1):
                    y1, y2 = k - x1, k - x2
                    if grid[x1][y1] == -1 or grid[x2][y2] == -1:
                        continue
                    best = max(dp[x1][x2], dp[x1][x2 - 1] if x2 else -1)
                    if x1:
                        best = max(best, dp[x1 - 1][x2], dp[x1 - 1][x2 - 1])
                    if best < 0:
                        continue
                    nd[x1][x2] = best + grid[x1][y1] + (grid[x2][y2] if x1 != x2 else 0)
            dp = nd
        return max(dp[n - 1][n - 1], 0)

载入编辑器,自己跑一遍 →

动态规划 专题的其他题目

查看 动态规划 全部题目 →