LeetCode.864-题解

题目描述:

给定一个二维网格 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))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值