题目描述:
给定一个二维网格 grid。 “.” 代表一个空房间, “#” 代表一堵墙, “@” 是起点,(“a”, “b”, …)代表钥匙,(“A”, “B”, …)代表锁。
我们从起点开始出发,一次移动是指向四个基本方向之一行走一个单位空间。我们不能在网格外面行走,也无法穿过一堵墙。如果途经一个钥匙,我们就把它捡起来。除非我们手里有对应的钥匙,否则无法通过锁。
假设 K 为钥匙/锁的个数,且满足 1 <= K <= 6,字母表中的前 K 个字母在网格中都有自己对应的一个小写和一个大写字母。换言之,每个锁有唯一对应的钥匙,每个钥匙也有唯一对应的锁。另外,代表钥匙和锁的字母互为大小写并按字母顺序排列。
返回获取所有钥匙所需要的移动的最少次数。如果无法获取所有钥匙,返回 -1 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shortest-path-to-get-all-keys
解题思路:
使用最短路径Dijkstra算法解决,最多有6对钥匙和锁,因此引入二进制记录钥匙的获取状态,使用三维数组[r][c][state]解决。
Dijkstra算法步骤,首先,将图中各个结点之间的距离求出来,然后根据算法思想从起点开始遍历图,当获取到所有钥匙或者图遍历结束,输出结果。
import collections
import heapq
class Solution(object):
def shortestpathAllKeys(self, grid):
R, C = len(grid), len(grid[0])
# 求出图中所有字符结点坐标
point = {V : (r, c)
for r, row in enumerate(grid)
for c, V in enumerate(row)
if V not in '.#'}
# 返回结点可以走的下一个结点坐标
def checkPoint(r, c):
for cr, cc in ((r-1, c), (r, c+1), (r+1, c), (r, c-1)):
if 0 <= cr < R and 0 <= cc < C:
yield cr, cc
# 统计当前元素可以直接到达的元素的距离
def BFS(source):
r, c = point[source]
seen = [[False]*C for _ in range(R)]
seen[r][c] = True
# 双向队列,方便队头删除和队尾插入
queue = collections.deque([(r, c, 0)])
dist = {}
# 用队列来存储图结点状态
while queue:
r, c, d = queue.popleft()
# 当其为非起始点的其他字符结点时,记录步数
if source != grid[r][c] != '.':
dist[grid[r][c]] = d
continue
# 当前结点为.且没有被访问时,更新结点结点和步数
for cr, cc in checkPoint(r, c):
if grid[cr][cc] != '#' and not seen[cr][cc]:
queue.append((cr, cc, d+1))
seen[cr][cc] = True
return dist
dists = {place: BFS(place) for place in point}
targetState = 2 ** sum(p.islower() for p in point) -1
# 使用最小堆存储结点状态
pq = [(0, '@', 0)]
finalDist = collections.defaultdict(lambda : float('inf'))
finalDist['@', 0] = 0
while pq:
d, place, state = heapq.heappop(pq)
if finalDist[place, state] < d:
continue
if state == targetState:
return d
for destination, d2 in dists[place].items():
state2 = state
# 当前结点是钥匙
if destination.islower():
state2 |= (1 << ord(destination) - ord('a'))
# 当前结点是锁
elif destination.isupper():
if not(state & (1 << ord(destination) - ord('A'))):
continue
# 更新堆栈结点状态
if d + d2 < finalDist[destination, state2]:
finalDist[destination, state2] = d + d2
heapq.heappush(pq, (d+d2, destination, state2))
return -1
if __name__ == '__main__':
grid = ["@.a.#","###.#","b.A.B"]
ss = Solution()
print(ss.shortestpathAllKeys(grid))