这是我参与8月更文挑战的第17天,活动详情查看:8月更文挑战
难度: 中等
题目描述
给你一个大小为 m x n 的网格和一个球。球的起始坐标为 [startRow, startColumn] 。你可以将球移到在四个方向上相邻的单元格内(可以穿过网格边界到达网格之外)。你 最多 可以移动 maxMove 次球。
给你五个整数 m、n、maxMove、startRow 以及 startColumn ,找出并返回可以将球移出边界的路径数量。因为答案可能非常大,返回对 109 + 7 取余 后的结果。
示例 1:
输入:m = 2, n = 2, maxMove = 2, startRow = 0, startColumn = 0 输出:6
示例 2:
输入:m = 1, n = 3, maxMove = 3, startRow = 0, startColumn = 1 输出:12
提示:
1 <= m, n <= 50 0 <= maxMove <= 50 0 <= startRow < m 0 <= startColumn < n
解题思路
-
最直接的方式是DFS递归解决,只要出界,那就是有一种结果,return 1return1,
-
否则就递归求解。但是直接递归,存在大量的重复计算,比如考虑移动kk步和移动k-1k−1步的时候,就可以有1步选择不同的位置出界(其实也可以选更多不同的步数)。
-
如果要采用递归的方式,应当考虑采用记忆化的方法,python提供了 @lru_cache(None) 装饰器,可以实现备忘的功能,加上 @lru_cache(None) 的代码和执行结果如下。
-
还有一种思考方式是使用动态规划,既然写出了递归,那么可以根据递归来改写成动态规划。
for di, dj in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
count=(count + self.findPaths(m, n, N-1, di, dj)) % 1000000007
这样就把递归深搜改写成了动态规划。
记忆化递归
class Solution:
@lru_cache(None) #通过修饰器实现记忆化
def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:
count = 0
if N < 0:
return count
if i < 0 or i >= m or j < 0 or j >= n:
return 1
for m, n in [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]:
count = (count + self.findPaths(m, n, N-1, m, n)) % 1000000007
return count
动态规划
class Solution:
def findPaths(self, m: int, n: int, N: int, i: int, j: int) -> int:
dp = [[[0]*(n+2) for _ in range(m+2)] for _ in range(N+1)]
if N == 0:
return 0
for k in range(N+1):
for x in range(m+2):
for y in range(n+2):
if x == 0 or y == 0 or x == m + 1 or y == n + 1: #相当于递归的基线条件
dp[k][x][y] = 1
else:
if k == 0:
continue
else: # 相当于递归调用部分
dp[k][x][y] = (dp[k][x][y] + dp[k-1][x-1][y] + \
dp[k-1][x+1][y] + dp[k-1][x][y-1] + \
dp[k-1][x][y+1]) % 1000000007
return dp[N][i+1][j+1]