↓快轉到主要內容
  1. LeetCode/

LeetCode 864: Shortest Path to Get All Keys

·2 分鐘· ·
LeetCode Hard Bfs Bit-Manipulation Matrix
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: hard 第一次嘗試:2026-09-05

  • 提交紀錄:Accepted → Accepted(整理一次)

解題思路
#

在網格上走,撿到鑰匙才能通過對應的鎖,求拿到所有鑰匙的最少步數。

只記位置不夠:同一格,拿著不同的鑰匙,接下來能走的地方不一樣。所以 BFS 的狀態是 (r, c, keys),keys 用位元遮罩表示(最多 6 把,2^6 = 64 種)。

  • 走到鑰匙:遮罩加上這把鑰匙。
  • 走到鎖:只有遮罩裡有對應的鑰匙才能走。
  • 遮罩等於「全部鑰匙」時,目前的步數就是答案。

我的解法
#

from collections import deque

class Solution:
    def shortestPathAllKeys(self, grid: List[str]) -> int:
        m, n = len(grid), len(grid[0])
        total = 0
        for r in range(m):
            for c in range(n):
                if grid[r][c] == "@":
                    start = (r, c)
                elif grid[r][c].islower():
                    total += 1
        full = (1 << total) - 1

        q = deque([(start[0], start[1], 0, 0)])            # r, c, keys, steps
        seen = {(start[0], start[1], 0)}
        while q:
            r, c, keys, steps = q.popleft()
            if keys == full:
                return steps
            for nr, nc in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
                if not (0 <= nr < m and 0 <= nc < n) or grid[nr][nc] == "#":
                    continue
                ch = grid[nr][nc]
                if ch.isupper() and not keys >> (ord(ch) - ord("A")) & 1:
                    continue                               # 沒有鑰匙,過不去
                nkeys = keys | (1 << (ord(ch) - ord("a"))) if ch.islower() else keys
                if (nr, nc, nkeys) not in seen:
                    seen.add((nr, nc, nkeys))
                    q.append((nr, nc, nkeys, steps + 1))
        return -1

複雜度
#

時間 O(mn · 2^k),k 是鑰匙數量。空間 O(mn · 2^k)。

要特別避免的錯誤
#

  • visited 只記位置:拿到鑰匙之後回頭走的路徑會被擋掉。
  • 鑰匙數量寫死成 6:要依網格裡實際的鑰匙數算出「全部」的遮罩。
  • 在網格上原地標記走過的格子:拿著不同鑰匙時會需要再經過同一格,標記要放在包含鑰匙遮罩的 seen 裡。

面試口說整理
#

因為同一格拿著不同的鑰匙,之後能走的範圍不同,我把 BFS 的狀態設成位置加上鑰匙的位元遮罩。走到鑰匙就把對應的位元加上去,走到鎖要檢查遮罩裡有沒有對應的鑰匙。visited 記整個狀態,第一次遮罩變成全滿時的步數就是答案。狀態數是 mn · 2^k,k 最多 6。

相關文章

LeetCode 2328: Number of Increasing Paths in a Grid
·2 分鐘
LeetCode Hard Dynamic-Programming Grid-Dp Memoization Topological-Sort Matrix
LeetCode 2328 解題筆記:嚴格遞增所以沒有環;dp[cell] = 1 + Σ dp[比它大的鄰居],依值由大到小處理(或記憶化 DFS),全部加起來
LeetCode 1178: Number of Valid Words for Each Puzzle
·1 分鐘
LeetCode Hard Bit-Manipulation Hash-Map Trie
LeetCode 1178 解題筆記:單字只在乎用了哪些字母,轉成 26-bit 遮罩計數,再枚舉謎題遮罩的所有子集合
LeetCode 2392: Build a Matrix With Conditions
·1 分鐘
LeetCode Hard Graph Topological-Sort Matrix
LeetCode 2392 解題筆記:列條件和欄條件互不影響,各做一次拓撲排序;數字 x 放在列順序和欄順序的位置,任一邊有環就回傳空矩陣
LeetCode 3548: Equal Sum Grid Partition II
·2 分鐘
LeetCode Hard Prefix-Sum Hash-Table Matrix Enumeration
LeetCode 3548 解題筆記:枚舉橫切(直切用轉置),兩邊差值要等於較大那邊某一格的值;那一邊只有一列或一欄時只能去掉兩端,否則任何一格都可以
LeetCode 980: Unique Paths III
·1 分鐘
LeetCode Hard Backtracking Matrix Dfs
LeetCode 980 解題筆記:網格回溯,原地標記走過的格子,到終點時檢查是否走過所有空格
LeetCode 3989: Maximum Consistent Columns in a Grid
·1 分鐘
LeetCode Hard Dynamic-Programming Matrix Array
LeetCode 3989 解題筆記:保留的欄位是一個子序列,只要相鄰的兩欄相容;LIS 型 DP,dp[j] = 以第 j 欄結尾的最長合法序列