返回
为解决 leetcode 第 2400 题提供独到见解:记忆化搜索在减少计算量中的妙用
后端
2023-10-04 08:25:06
我们来探索一下《Number of Ways to Reach a Position After Exactly k Steps》——LeetCode 第 2400 题。这题属于中等难度,利用记忆化搜索技巧可以大大减少计算量。现在,我们就一起深入了解一下!
引言
在解决编程难题的过程中,有时候我们可能会被纷繁复杂的计算量吓退。而 LeetCode 第 2400 题就正是这样一个例子,它考验我们用巧妙的算法技巧来减少计算量。在这个问题中,你需要计算在给定的棋盘上,从原点走到指定位置的方案数,且只能移动指定次数。
def numWays(self, n: int, k: int) -> int:
MOD = 10**9 + 7
dp = {}
def dfs(x, y, moves):
if (x, y, moves) in dp:
return dp[(x, y, moves)]
if x == n - 1 and y == n - 1 and moves == 0:
return 1
if x < 0 or x >= n or y < 0 or y >= n or moves < 0:
return 0
dp[(x, y, moves)] = (dfs(x - 1, y, moves - 1) + dfs(x + 1, y, moves - 1) +
dfs(x, y - 1, moves - 1) + dfs(x, y + 1, moves - 1)) % MOD
return dp[(x, y, moves)]
return dfs(0, 0, k)
记忆化搜索
记忆化搜索(Memoization)是一种非常重要的优化技巧,能够将递归问题大幅简化,且过程相对轻松。其核心思想是,对于已经计算过的子问题,将结果存储起来,避免重复计算。这样一来,当遇到相同子问题时,我们可以直接从存储的结果中获取答案,从而减少不必要的计算,提高运行效率。
记忆化搜索的运作方式是,我们对每个子问题的输入值进行哈希运算,将得到的哈希值作为子问题的键,并将子问题的解作为键对应的值。当我们再次遇到相同的子问题时,可以直接通过哈希值从存储中取出解,而无需重新计算。
以本题为例,我们可以将当前位置的坐标和剩余步数作为子问题的输入值,将方案数作为子问题的解。当我们再次遇到相同的位置和剩余步数时,直接从存储中取出方案数即可。
具体步骤
-
定义状态和存储结果的结构
- 在本题中,我们将状态定义为当前位置的坐标和剩余步数。
- 将子问题的解作为键对应的值,存储起来,避免重复计算。
-
计算初始状态的解
- 将棋盘的原点坐标和给定的步数作为初始状态,计算方案数。
-
遍历所有可能的状态
- 从初始状态开始,枚举所有可能的位置,如果位置合法且剩余步数大于 0,则将该位置作为新的状态。
-
判断状态是否已经计算过
- 如果当前状态已经在存储结构中,则直接取出解。
- 如果当前状态不在存储结构中,则计算该状态的解并存储起来。
-
返回最终结果
- 当遍历完所有可能的状态后,返回最终状态的解。
Python 实现
def numWays(n: int, k: int) -> int:
# 定义哈希函数
def hash_function(x, y, moves):
return (x << 16) | (y << 8) | moves
# 定义哈希表
memo = {}
# 计算初始状态的解
initial_state = (0, 0, k)
initial_result = dfs(initial_state)
return initial_result
# 定义递归函数
def dfs(state):
# 获取状态信息
x, y, moves = state
# 判断状态是否已经计算过
if state in memo:
return memo[state]
# 判断是否达到终点
if x == n - 1 and y == n - 1 and moves == 0:
return 1
# 判断是否越界或步数不足
if x < 0 or x >= n or y < 0 or y >= n or moves < 0:
return 0
# 计算当前状态的解
result = (dfs((x - 1, y, moves - 1)) + dfs((x + 1, y, moves - 1)) +
dfs((x, y - 1, moves - 1)) + dfs((x, y + 1, moves - 1))) % MOD
# 将状态及其解存储到哈希表中
memo[state] = result
return result