BFS
就BFS
1. 什麼是 BFS?為什麼能找最短路?
相信各位沒玩過 Minecraft 應該也看過 想像你在 Minecraft 的地上到了一盆水,那那個水會從中心慢慢往外擴散。
- 第
0格:水源位置(起點) - 第
1格:一步可達 - 第
2格:兩步可達 - 以此類推…
如果地上有牆或玻璃(障礙),水就過不去必須要用繞的。而哪一格最先被水淹到,代表他離起點的步數最少。
BFS(Breadth-First Search,廣度優先搜尋)的實作原理就像是這樣,從中心一步一步往外擴散。

正式一點來說:
- 從中心開始每走一步都算
1(沒有快慢之分,除非題目特別要求) BFS會先把距離1的格子全部處理完,再處理距離2、距離3…- 所以「第一次」碰到目標時,距離必然是最短步數。
這就是為什麼 BFS 能解最短路。
寫 BFS 的三個重點:
- 用之前在
STL上過的queue(先進先出,就像一排人一個一個往外擴) - 入隊時就標記「來過了」(避免重複入隊)
- 一摸到終點就收工(第一次碰到一定會是最短的)
2. BFS vs DFS
- BFS:一圈一圈灑出去,像水流。第一次碰到目標就是最短。
- DFS:沿著一條路一直鑽到底,可能先繞遠路才遇到目標,不保證最短。
要找最短路 → 用 BFS。
想要找「有沒有路」或「整個形狀」 → 用 DFS。
白話點就是如果題目有要求最短路,基本上第一個要想到的是 BFS ,不過其實大多數能用 BFS 的題目 DFS 也可以,反過來也是,但就是好不好寫跟速度的差別。
另一個判斷的方式是,通常 DFS 都會寫到遞迴找路,而 BFS 就不會。
3. 例題:b348: nowob的機廳冒險
題意可以過去看一下(這次我題目沒搞事了,應該比較好懂)
地圖說明(四方向移動:上、下、左、右):
- 0:空氣
- 1:nowob 起點(只有一個)
- 2:maimai(可能很多個)
- 3:地雷女(障礙,不能走)
要做的事:
- 輸出:從起點走到最近一台 maimai 的最短步數
- 若走不到任何 maimai,輸出
-1
移動規則:
- 只能上下左右走一格(四方向),不能走斜的(真的跟Minecraft的水一樣)
- 不能走出地圖外(就判一下邊界)
- 不能走進
3(不然就要上靠北版了) - 可以走在
0 - 踩到
2就代表到達一台 maimai,直接輸出然後return 0;
距離怎麼算?
- 起點站著不動是 0 步
- 往相鄰的一格走是 1 步
- 再走一格就是 2 步,以此類推
- 你要輸出的就是「到第一台maimai的最少步數」
什麼時候輸出 -1?
- 地圖上根本沒有
2 - 或者所有
2都被3擋住,起點就算繞路也到不了
看一個範例(一步一步看懂)
範例輸入:
5 6
0 0 0 0 0 0
0 1 0 3 0 0
0 3 0 3 2 0
3 0 3 0 0 0
0 2 0 0 0 2
你會看到:
- 起點
1在第 2 行第 2 列(從0-based數的話是 (1,1)) - 有好幾個
2(maimai),像 (2,4)、(4,1)、(4,5) - 其中一條最短路:右、上、右、右、下、下
- 走了 6 步,答案就是 6
- 你不需要輸出路徑,只要輸出步數就好
這題要怎麼下手
一樣把 BFS 當作水在擴散,照這樣做:
- 先把整張地圖讀進來,找到起點
1的位置 (sr, sc) - 準備一張「距離表」
dist,先全部設成 -1(代表還沒來過) - 把起點丟進
queue,並把 dist[sr][sc] 設成 0 - 進入迴圈:
- 從
queue拿出一格 (r, c) - 往四個方向看 (nr, nc)
- 如果越界、或那格是
3、或dist不是 -1(來過了),就略過 - 否則把 dist[nr][nc] = dist[r][c] + 1,並把這格丟進
queue - 如果這格剛好是
2,立刻輸出 dist[nr][nc],結束(第一次遇到 = 最短!)
- 從
queue都清空了還沒遇到2,就輸出-1
你可以把它想成:
- 0 步:只有起點
- 1 步:離起點一格的所有位置
- 2 步:離起點兩格的所有位置
- …
- 哪一層第一次踩到
2,那層數就是答案
基本上 BFS 就是這樣,雖然會有很多變體但本身邏輯差不多,不過偷偷講一下,BFS 不會是最短路的最佳解,至於怎麼做最佳解可以來參加下學期的進階班:D