给定一个二维网格 grid。 "." 代表一个空房间, "#" 代表一堵墙, "@" 是起点,("a", "b", ...)代表钥匙,("A", "B", ...)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
示例 1:
输入:["@.a.#","###.#","b.A.B"]
输出:8
示例 2:
输入:["@..aA","..B#.","....b"]
输出:6
提示:
1 <= grid.length <= 30
1 <= grid[0].length <= 30
grid[i][j] 只含有 '.', '#', '@', 'a'-'f' 以及 'A'-'F'
钥匙的数目范围是 [1, 6],每个钥匙都对应一个不同的字母,正好打开一个对应的锁。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-path-to-get-all-keys
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution:
def shortestPathAllKeys(self, grid: List[str]) -> int:
from queue import Queue
m = len(grid)
n = len(grid[0])
q = Queue()
start_i, start_j = -1,-1
key_num = 0#钥匙的总数
for i in range(m):
for j in range(n):
if(grid[i][j] == '@'):
start_i, start_j = i,j
if(grid[i][j].islower()):
key_num += 1
q.put((start_i,start_j,(),0))#tuple(当前行数,当前列数,当前钥匙,当前开销)
best_stat = {(start_i,start_j,()):0}#字典类型,key=(当前行数,当前列数,当前钥匙)的最小开销
while not q.empty():
cur_i,cur_j,keys,cost = q.get()#取当前队列头部的元素
if len(keys) == key_num:
return cost
for ii,jj in [(cur_i - 1,cur_j),(cur_i + 1,cur_j),(cur_i,cur_j - 1),(cur_i,cur_j + 1)]:
if(ii >=0 and ii < m and jj >= 0 and jj < n):
if grid[ii][jj] in ['@','.']:
if (ii,jj,keys) not in best_stat or cost + 1 < best_stat[(ii,jj,keys)]:
best_stat[(ii,jj,keys)] = cost + 1
q.put((ii,jj,keys,cost + 1))
if grid[ii][jj].islower():
#将新取得的钥匙加入到set中
new_keys = tuple(set(keys) | {grid[ii][jj]})
if (ii,jj,new_keys) not in best_stat or cost + 1 < best_stat[(ii,jj,new_keys)]:
best_stat[(ii,jj,new_keys)] = cost + 1
q.put((ii,jj,new_keys,cost + 1))
#将大写字母转换为小写字母并与其判断是否有钥匙
if grid[ii][jj].isupper() and chr(ord(grid[ii][jj]) - ord('A') + ord('a')) in keys:
if (ii,jj,keys) not in best_stat or cost + 1 < best_stat[(ii,jj,keys)]:
best_stat[(ii,jj,keys)] = cost + 1
q.put((ii,jj,keys,cost + 1))
return -1