混亂的地鐵(廣度優先搜尋)
題目描述:
在某一個城市中地鐵網極度混亂。一條地鐵線路上有 n 個地鐵站,分別編號為 1 到 n。地鐵線路上的每一個站都會停靠地鐵,每一個地鐵站上都有一個數字 m,代表從此站出發乘客必須乘坐的站數。
每個地鐵站都有通往兩個方向的地鐵。因此既可以向編號大的方向前進 m 站,也可以向編號小的方向前進 m
站。但如果前進後超出了地鐵站的範圍,則該地鐵不可被乘坐。例如編號為 1 的地鐵上的數字為 3,那麼在該地鐵站上車,可以向正方向坐車到 4
號地鐵站。但不能反方向坐車到 -2 號地鐵站,因為 -2 號地鐵站不存在。現在乘客從 A 號地鐵站出發,想要到達 B
號地鐵站,求他能否到達,最少要搭乘多少次地鐵?
題目分析:
在分析這個問題前先舉一個具體的例子。如下圖 所示,如果一共有 5 個地鐵站,其編號依次為 1,2,3,4,5。而每個地鐵站的數字分別為 2,4,1,2,3。現在我們來分析乘客從 1 號地鐵站上車,想要到達 2 號地鐵站的情況。
如果乘客從 1 號地鐵站出發,則他有兩個選擇,向正方向坐車,或向反方向坐車。1 號車站的數字為 2,因此可以到達 3 號地鐵站和 -1 號地鐵站。又因為 -1 號地鐵站並不存在,所以從 1 號地鐵站出發只能到達 3 號地鐵站。綜上我們可以得出結論:從 1 號地鐵站出發,到達 3 號地鐵站最少要坐一趟地鐵,如下圖所示。
此時到達 3 號地鐵站。3 號地鐵站的數字為 1,因此可以向正方向坐 1 站車到達 4 號地鐵站,也可以向反方向坐 1 站車直接到達目的地 2 號地鐵站。同樣我們也可以得出結論:從 1 號地鐵站出發,到達 2 號地鐵站和 4 號地鐵站至少需要坐兩趟地鐵,如圖 下所示。
此時我們已經遍歷出結果:從 1 號地鐵站出發到達 2 號地鐵站至少要乘坐兩次地鐵。如果我們的迴圈跳出條件設立的是到達目的地或者佇列為空,那麼遍歷已經結束。但是如果我們的迴圈跳出條件只有佇列為空,那麼我們還需進一步遍歷,因為此時佇列中仍有兩個元素:4 號地鐵站和 2 號地鐵站。
值得注意的是,當我們從 4 號地鐵站出發時,如果向正方向乘坐 2 站地鐵(4 號地鐵站上的數字為
2),將超出地鐵站範圍,因此無法向正方向乘坐地鐵。如果向反方向乘坐 2 站地鐵,將會到達目的地 2 號地鐵站。當我們按照從 1
號地鐵站出發,到達 3 號地鐵站,再到達 4 號地鐵站,最終到達 2 號地鐵站的話需要乘坐 3
趟地鐵,沒有上一種地鐵乘坐方案便利,應當淘汰這種方案,如下圖 所示。
但如果仔細思考使用佇列資料結構的廣度優先搜尋演算法,不難推匯出最優的方案一定比次優的方案先進入佇列中。因為廣度優先搜尋演算法具有層次性,每一次遍歷距離最近或距離相同的目標。如果距離近是問題答案最重要的考察因素,則廣度優先搜尋演算法會自動地優先搜尋距離較近的目標,最快地找到問題的答案。
所以,我們只需建立一個數組或列表來記錄是否已經遍歷過一個目標,防止重複遍歷同一目標導致無限迴圈即可,不需再擔心最優的答案可能出現在重複的遍歷過程中。
依舊使用廣度優先搜尋演算法的模板,我們在一個 while 迴圈內對佇列結構進行遍歷操作,迴圈的跳出條件依舊為佇列為空。
程式碼:
from queue import Queue
move=[2,4,1,2,3] #move列表裡儲存每一個地鐵站的數字
q=Queue()
start,end,num=map(int,input().split()) #出發點、目的地、總共的地鐵站數量
station=[-1 for i in range(num+1)] #列表儲存從出發點到對應地鐵站需乘坐地鐵的趟數,-1代表無法到達
q.put(start)
station[start]=0 #出發點到自身地鐵站時不用乘坐任何地鐵,賦值為0
while not q.empty():
cur=q.get() #取出佇列第一項,並將其移出佇列
left=cur-move[cur-1] #計算出向反方向乘坐會到達哪個地鐵站
right=cur+move[cur-1] #計算出向正方向乘坐會到達哪個地鐵站
if left>=1 and station[left]==-1:
q.put(left)
station[left]=station[cur]+1
if right<=num and station[right]==-1:
q.put(right)
station[right]=station[cur]+1
if __name__=='__main__':
print(station[end]) #輸出答案