要解決的問題:
給定一個迷宮,此迷宮中有且僅有一個入口和出口,其中設有若干檢查點,要求從入口開始,經過所有檢查點后到達出口所需的最短路徑。其中路徑中允許多次經過入口或出口或某檢查點,但路徑的開始和結尾必須分別是入口和出口。更形象一點就是要把圖中所有的寶藏找出來帶出去的問題。
連設計算法+寫算法實現的論文+編寫代碼和制作演示動畫,花費了四天時間,還是小有收獲的趕腳。算法的核心描述就是先假設已經有一個最優路徑,然后插入一個新的檢查點時,如何使插入導致的路徑增量最小。
這種先假設存在最優路徑然后再在這條假設的路徑中插入新的結點的思想其實就是貪心算法,出現在很多算法當中,並且十分行之有效,其基本模型就是持有兩個關鍵集合,根據條件改變集合元素來動態規划集合中元素的組織方式。此題中的尋寶算法十分類似於貨郎擔問題,區別在於,貨郎在尋找旅行路線時不能經過已經走過的城市,且最終要回到出發點,而這里的模型是可以經過已經走過的城市,但最終不是要回到出發點,而是以達到一個指定點結束。但本質上都需要兩個動態集合來保存這些代表地點的結點。貨郎擔問題相對於此問題的簡單之處表現在其不重復的特點上,因為不重復,所以每個結點必有單前驅與單后繼,集合中元素都不相同,組合起來就很簡單。而此問題由於可以多次經過同一個節點,所以在一條路徑上就存在多個相同的結點,這就導致了同一結點有多前驅或多后繼的問題。本問題解決的關鍵就是要解決這種多前驅與多后繼的問題,也就是要決定同一結點如何在集合中表現出多種身份的方式。我在設計此算法時,到此便知道這就是問題的核心了,最終想到的辦法就是為多前驅或多后繼的結點建立多個虛擬結點,這些虛擬結點實質上共享同一個位置,但卻分別有着不同的前驅與后繼,因此可以構成一條完成的路線。然后問題就轉移到如何在兩個集合中標識這些虛擬結點的身份了。剩下的問題不再是難題,除了需要計算在插入新結點時如何查找最優插入區間、如何計算插入后帶來的代價增量、如何決定是采用正常插入模式還是采用創建新的虛擬結點的插入方式等問題了。其中用到了算法導論一書中介紹的“三角不等式(兩邊之和大於第三邊,兩邊之差小於第三邊)”原理來計算和比較代價增量。
最后,如設計算法時所描述,這其實是一個NP完全問題,無法驗證它是最優的,插入時存在穩定性改變的問題,但絕大多數情況下,得到的結果就是我們想要的最優結果。同時,整個尋路過程中,BFS算法的時間復雜度為O(logn),其中包括前向找出口的logn的時間和回退的logn的時間以及路徑方向整理的logn的時間,這個總的時間復雜度雖然不算特別大,但卻經常需要計算兩點間的最短路徑,而在很多情況下,需要重復計算上一次所得的結果。因此在算法中適時保存和處理中間結果將能夠大大提高算法的時間性能。
最終的運行結果如圖所示: