📅  最后修改于: 2023-12-03 15:26:49.850000             🧑  作者: Mango
给定一个 m*n 的矩阵,矩阵中每个单元格要么是空格子,要么是障碍物。现在你需要检查给定矩阵中是否存在从开始到结束单元格的路径,而且在这个路径上,允许进行最多 K 次障碍物移动,其中 K 是一个非负整数。
这是一道典型的动态规划问题。我们可以用一个三维数组 dp[i][j][k]
来记录当前在第 i 行第 j 列,移动了 k 次障碍的情况下能否到达目标。该数组的值为一个布尔值,若为 True 则表示当前的状态能够到达目标,反之则不能。
核心思路:因为有 k 次移动障碍的机会,我们可以将问题转化为 k 个子问题。假设现在我们在某个格子 (i, j),还有剩余 k' 次障碍物移动的机会,那么我们可以进行一个决策:
根据上面的分析,可以得出状态转移方程:
$$dp[i][j][k]=\begin{cases} True& \quad\text{ (i, j) 到达目标 }\ False& \quad\text{ 矩阵中 (i, j) 不是空格子 }\ True& \quad\text{ 下一个格子是空地:} dp[i+\delta_x][j+\delta_y][k]=True \ & \quad \quad \quad\quad \quad \quad\text{($\delta_x$,$\delta_y$)为(i,j)到(x,y)的偏移量} \ True& \quad\text{下一个格子是障碍物:} dp[i+\delta_x][j+\delta_y][k-1]=True \ & \quad\quad\text{并且 k>0} \end{cases}$$
def hasPath(matrix: List[List[int]], k: int) -> bool:
m, n = len(matrix), len(matrix[0])
# 定义状态数组,初始化为 False
dp = [[[False] * (k + 1) for _ in range(n)] for _ in range(m)]
# 初始化,如果位置 (0,0) 是空地,那么可以从起点出发
if matrix[0][0] == 0:
dp[0][0][0] = True
# 遍历整个矩阵
for k1 in range(k + 1):
for i in range(m):
for j in range(n):
# 当前位置是障碍物,跳过
if matrix[i][j] == 1:
continue
# 分别考虑从上、左、下、右四个方向移动的情况
for delta in [(0, -1), (-1, 0), (0, 1), (1, 0)]:
x = i + delta[0]
y = j + delta[1]
# 格子越界,或者是障碍物,无法到达,跳过
if x < 0 or x >= m or y < 0 or y >= n or matrix[x][y] == 1:
continue
# 如果下一个格子是空地,那么 k 不变(没有消耗掉机会)
if dp[x][y][k1]:
dp[i][j][k1] = True
break
# 如果下一个格子是障碍物,那么消耗掉一次机会
if k1 > 0 and dp[x][y][k1 - 1]:
dp[i][j][k1] = True
break
# 返回 dp[0][0][k],即是否存在一条路径记过K次障碍物到达终点
return dp[0][0][k]
时间复杂度:$O(mnk)$,其中 $m$ 是矩阵的行数, $n$ 是矩阵的列数, $k$ 是最多允许的障碍物移动次数。状态总共有 $m\cdot n\cdot k$ 个,每个状态需要枚举四个方向,因此时间复杂度为 $O(4\cdot{m\cdot n\cdot k}) = O(mnk)$。
空间复杂度:$O(mnk)$。状态数组 dp
长度为 $m\cdot n\cdot k$。