864. 获取所有钥匙的最短路径
给定一个二维网格 grid ,其中:
-
. 代表一个空房间 # 代表一堵 @ 是起点 小写字母代表钥匙 大写字母代表锁
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 k 为 钥匙/锁 的个数,且满足 1 <= k <= 6,字母表中的前 k 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
示例 1:
输入:grid = ["@.a.#","###.#","b.A.B"] 输出:8 解释:目标是获得所有钥匙,而不是打开所有锁。
BFS 求最短路径,需要注意的是,由于这个题同一个位置可以重复访问,加入 visited 的必须是 (位置坐标, 已获得钥匙的分布),state 记录当前收集过的钥匙。如果遇到锁,判断钥匙是否已经有了
class Solution: def shortestPathAllKeys(self, grid: List[str]) -> int: m = len(grid) n = len(grid[0]) keys = abcdef locks = ABCDEF cnt = 0 for i in range(m): for j in range(n): if grid[i][j] == @: # 开始位置 begin = (i, j) if grid[i][j] in keys: cnt += 1 # 钥匙总数 q = deque([(begin[0], begin[1], )]) vis = set() dir = [(0,1), (0,-1), (-1,0), (1,0)] res = 0 while q: for _ in range(len(q)): # 按层遍历 x, y, state = q.popleft() if (x, y, state) in vis: continue if len(state) == cnt: # 如果钥匙已经收集完了 return res vis.add((x, y, state)) for dx, dy in dir: i = x + dx j = y + dy if not (0 <= i < m and 0 <= j < n and grid[i][j] != #): continue if grid[i][j] in locks and grid[i][j].lower() in state: # 如果当前是锁且已经有钥匙了 q.append((i, j, state)) elif grid[i][j] in keys: # 如果当前是钥匙 if grid[i][j] not in state: # 未收集过该钥匙,则收集 q.append((i, j, state + grid[i][j])) else: # 收集过 q.append((i,j, state)) elif grid[i][j] in .@: q.append((i, j, state)) res += 1 return -1