返回
从 LeetCode 741 摘樱桃学经典线性 DP 应用题
后端
2023-12-28 20:14:40
从 LeetCode 741 摘樱桃学经典线性 DP 应用题
前言
在 LeetCode 的浩瀚题海中,有许多经典题目可以帮助我们掌握各种编程算法和数据结构。其中,线性动态规划(DP)是解决许多问题的重要技巧。LeetCode 741 摘樱桃就是一道经典的线性 DP 题目。在本篇文章中,我们将详细解析这道题目,一步步带领您理解算法思想和实现步骤,并提供详细的示例和清晰的讲解,帮助您深入理解线性动态规划的应用。
题目
在一个 N \times N 的网格中,每个格子代表了一块樱桃地。每个格子由以下三种数字之一组成:
0
表示一个空地1
表示一棵樱桃树2
表示一个障碍物
您从网格的左上角出发,每次只能向右或向下走一步。您想收集尽可能多的樱桃,但不能经过障碍物。
算法思想
解决这道题目的关键在于理解线性动态规划的思想。线性动态规划是一种自底向上的算法,它将问题分解成一系列子问题,然后逐步解决这些子问题,最终得到问题的整体解决方案。
对于这道题目,我们可以将问题分解成以下几个子问题:
- 当您到达网格的某个格子时,您可以选择向右或向下走一步。
- 每次移动,您都可以收集该格子中的樱桃。
- 您不能经过障碍物。
那么,对于每个格子,我们都可以计算出从该格子出发能够收集到的最大樱桃数量。这个数量取决于以下几个因素:
- 该格子本身的樱桃数量
- 从该格子向右走一步所能收集到的最大樱桃数量
- 从该格子向下走一步所能收集到的最大樱桃数量
我们只需要找到一个公式,将这些因素结合起来,就可以计算出每个格子的最大樱桃数量。
算法实现
根据上述算法思想,我们可以将算法实现如下:
def cherry_picking(grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
n = len(grid)
dp = [[[-1 for _ in range(3)] for _ in range(n)] for _ in range(n)]
def dfs(x, y, state):
"""
:type x: int
:type y: int
:type state: int
:rtype: int
"""
if x == n - 1 and y == n - 1:
return 0
if dp[x][y][state] != -1:
return dp[x][y][state]
res = 0
if state == 0:
res = max(dfs(x + 1, y, 1), dfs(x, y + 1, 2))
elif state == 1:
res = grid[x][y] + max(dfs(x + 1, y, 1), dfs(x, y + 1, 0))
else:
res = grid[x][y] + max(dfs(x + 1, y, 2), dfs(x, y + 1, 1))
dp[x][y][state] = res
return res
return dfs(0, 0, 0)
算法分析
算法的时间复杂度为 O(3n^2),其中 n 是网格的大小。这是因为对于每个格子,我们需要计算三种不同的状态,因此算法需要遍历网格中的每个格子三次。
算法的空间复杂度也为 O(3n^2),这是因为我们需要存储每个格子的三种不同状态。
结语
通过 LeetCode 741 摘樱桃这道经典题目,我们深入理解了线性动态规划的思想和应用。我们还详细解析了算法的实现细节,并提供了详细的示例和清晰的讲解。希望这篇文章能够帮助您掌握线性动态规划这一重要算法技巧,并将其应用到更多的问题中去。