📌  相关文章
📜  检查给定矩阵中是否存在从开始到结束单元格的路径,障碍物最多 K 次移动(1)

📅  最后修改于: 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' 次障碍物移动的机会,那么我们可以进行一个决策:

  • 如果下一个要移动的格子 (x,y) 是空地,那么此时我们的 k' 次机会不变,因为没有消耗掉一次障碍物移动的机会。
  • 如果下一个要移动的格子 (x,y) 是障碍物,此时有两种决策,一种是使用一次障碍物移动的机会,转移到 (x,y),此时剩余的机会数不变,即 k=k'-1。另一种是放弃移动到 (x,y),直接考虑下一个格子,此时 k=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$。