夜深人靜寫算法(1):搜索入門

jopen 9年前發布 | 55K 次閱讀 算法

原文出處: menjitianya  

一、深度優先搜索
1、DFS
2、基于DFS的記憶化搜索
3、基于DFS的剪枝
1) 可行性剪枝
2) 最優性剪枝
4、基于DFS的A* (迭代加深,IDA*)

二、廣度優先搜索
1、BFS
2、基于BFS的A*
3、雙向廣搜

三、搜索題集整理

一、深度優先搜索

1、DFS
1) 算法原理

深度優先搜索即 Depth First Search,是圖遍歷算法的一種。用一句話概括就是:“一直往下走,走不通回頭,換條路再走,直到無路可走”。

DFS的具體算法描述為選擇一個起始點v作為當前結點,執行如下操作:
a. 訪問 當前結點,并且標記該結點已被訪問,然后跳轉到b;
b. 如果存在一個和 當前結點 相鄰并且尚未被訪問的結點u,則將u設為 當前結點,繼續執行a;
c. 如果不存在這樣的u,則進行回溯,回溯的過程就是回退 當前結點

上述所說的當前結點需要用一個棧來維護每次訪問到的結點入棧,回溯的時候出棧(也可以用遞歸實現,更加方便易懂)。

如圖1所示,對以下圖以深度優先的方式進行遍歷,假設起點是1,訪問順序為1 -> 2 -> 4,由于結點4沒有未訪問的相鄰結點,所以這里需要回溯到2,然后發現2還有未訪問的相鄰結點5,于是繼續訪問2 -> 5 -> 6 -> 3 -> 7,這時候7回溯到3,3回溯到6,6回溯到5,5回溯到2,最后2回溯到起點1,1已經沒有未訪問的結點了,搜索終止,圖中圓圈代表路點,紅色箭頭表示搜索路徑,藍色虛線表示回溯路徑。

 夜深人靜寫算法(1):搜索入門圖 1

2) 算法實現

深搜最簡單的實現就是遞歸,寫成偽代碼如下:

def DFS(v):
    visited[v] = true
    dosomething(v)
    for u in adjcent_list[v]:
        if visited[u] is false:
            DFS(u)

其中dosomething表示訪問時具體要干的事情,根據情況而定,并且DFS是允許有返回值的。

3) 基礎應用

a. 求N的階乘;

令f(N) = N!,那么有f(N) = N * f(N-1) (其中N>0)。由于滿足遞歸的性質,可以認為是一個N個結點的圖,結點 i (i >= 1 ) 到結點 i-1 有一條權值為i的有向邊,從N開始深度優先遍歷,遍歷的終點是結點0,返回1(因為0! = 1)。如圖2所示,N!的遞歸計算看成是一個深度優先遍歷的過程,并且每次回溯的時候會將遍歷的結果返回給上一個結點(這只是一個思想,并不代表這是求N!的高效算法)。

 夜深人靜寫算法(1):搜索入門
圖 2

b. 求斐波那契數列的第N項;

令g(N) = g(N-1) + g(N-2), (N > 2),其中g(1) = g(2) = 1,同樣可以利用圖論的思想,從結點N向N-1和N-2分別引一條權值為1的有向邊,每次求g(N)就是以N作為起點,對N進行深度優先遍歷,然后將N-1和N-2回溯的結果相加作為N結點的值,即g(N)。

這里會帶來一個問題,g(n)的計算需要用到g(n-1)和g(n-2),而g(n-1)的計算需要用到g(n-2)和g(n-3),所以我們發現g(n-2)被用到了兩次,而且每個結點都存在這個問題,這樣就使得整個算法的復雜度變成指數級了,為了規避這個問題,下面會講到基于深搜的記憶化搜索。

 夜深人靜寫算法(1):搜索入門
圖 3

c. 求N個數的全排列;

全排列的種數是N!,要求按照字典序輸出。這是最典型的深搜問題。我們可以把N個數兩兩建立無向邊(即任意兩個結點之間都有邊,也就是一個N個結點的完全圖),然后對每個點作為起點,分別做一次深度優先遍歷,當所有點都已經標記時輸出當前的遍歷路徑,就是其中一個排列,這里需要注意,回溯的時候需要將原先標記的點的標記取消,否則只能輸出一個排列。如果要按照字典序,則需要在遍歷的時候保證每次遍歷都是按照結點從小到大的方式進行遍歷的。

 夜深人靜寫算法(1):搜索入門
圖 4

4) 高級應用

a. 枚舉:
數據范圍較小的的排列、組合的窮舉;

b. 容斥原理:
利用深搜計算一個公式,本質還是做枚舉;

c. 基于狀態壓縮的動態規劃:
一般解決棋盤擺放問題,k進制表示狀態,然后利用深搜進行狀態轉移;

d.記憶化搜索:
某個狀態已經被計算出來,就將它cache住,下次要用的時候不需要重新求,此所謂記憶化。下面會詳細講到記憶化搜索的應用范圍;

e.有向圖強連通分量:
經典的Tarjan算法;
求解2-sat問題的基礎;

f. 無向圖割邊割點和雙連通分量:
經典的Tarjan算法;

g. LCA:
最近公共祖先遞歸求解;

h.博弈:
利用深搜計算SG值;

i.二分圖最大匹配:
經典的匈牙利算法;
最小頂點覆蓋、最大獨立集、最小值支配集 向二分圖的轉化;

j.歐拉回路:
經典的圈套圈算法;

k. K短路:
依賴數據,數據不卡的話可以采用2分答案 + 深搜;也可以用廣搜 + A*

l. 線段樹
二分經典思想,配合深搜枚舉左右子樹;

m. 最大團
極大完全子圖的優化算法。

n. 最大流
EK算法求任意路徑中有涉及。

o. 樹形DP:
即樹形動態規劃,父結點的值由各個子結點計算得出。

2、基于DFS的記憶化搜索

1) 算法原理

上文中已經提到記憶化搜索,其實就是類似動態規劃的思想,每次將已經計算出來的狀態的值存儲到數組中,下次需要的時候直接讀數組中的值,避免重復計算。

來看個例子,如圖5所示,圖中的橙色小方塊就是傳說中的作者,他可以在一個N*M的棋盤上行走,但是只有兩個方向,一個是向右,一個是向下(如綠色箭頭所示),棋盤上有很多的金礦,走到格子上就能取走那里的金礦,每個格子的金礦數目不同(用藍色數字表示金礦的數量),問作者在這樣一個棋盤上最多可以拿到多少金礦。

 夜深人靜寫算法(1):搜索入門

圖 5

我們用函數DFS(i, j)表示從(1, 1)到(i, j)可以取得金礦的最大值,那么狀態轉移方程 DFS(i, j) = v[i][j] + max{ DFS(i, j-1), DFS(i-1, j) }(到達(i, j)這個點的金礦最大值的那條路徑要么是上面過來的,要么是左邊過來的),滿足遞歸性質就可以進行深度優先搜索了,于是遇到了和求斐波那契數列一樣的問題,DFS(i, j)可能會被計算兩次,每個結點都被計算兩次的話復雜度就是指數級了。

所以這里我們可以利用一個二維數組,令D[i][j] = DFS(i, j),初始化所有的D[i][j] = -1,表示尚未計算,每次搜索到(i, j)這個點時,檢查D[i][j]的值,如果為-1,則進行計算,將計算結果賦值給D[i][j];否則直接返回D[i][j]的值。

記憶化搜索雖然叫搜索,實際上還是一個動態規劃問題,能夠記憶化搜索的一般都能用動態規劃求解,但是記憶化搜索的編碼更加直觀、易寫。

3、基于DFS的剪枝

1) 算法原理

搜索的過程可以看作是從樹根出發,遍歷一棵倒置的樹——搜索樹的過程。而剪枝,顧名思義,就是通過某種判斷,避免一些不必要的遍歷過程,形象的說,就是剪去了搜索樹中的某些“枝條”,故稱剪枝(原話取自1999年OI國家集訓隊論文《搜索方法中的剪枝優化》(齊鑫))。如圖6所示,它是一棵利用深度優先搜索遍歷的搜索樹,可行解(或最優解)位于黃色的葉子結點,那么根結點的最左邊的子樹完全沒有必要搜索(因為不可能出解)。如果我們在搜索的過程中能夠清楚地知道哪些子樹不可能出解,就沒必要往下搜索了,也就是將連接不可能出解的子樹的那根“枝條”剪掉,圖中紅色的叉對應的“枝條”都是可以剪掉的。

 夜深人靜寫算法(1):搜索入門

圖6

好的剪枝可以大大提升程序的運行效率,那么問題來了,如何進行剪枝?我們先來看剪枝需要滿足什么原則:

a. 正確性
剪掉的子樹中如果存在可行解(或最優解),那么在其它的子樹中很可能搜不到解導致搜索失敗,所以剪枝的前提必須是要正確;

b. 準確性
剪枝要“準”。所謂“準”,就是要在保證在正確的前提下,盡可能多得剪枝。

c. 高效性
剪枝一般是通過一個函數來判斷當前搜索空間是否是一個合法空間,在每個結點都會調用到這個函數,所以這個函數的效率很重要。

剪枝大致可以分成兩類:可行性剪枝、最優性剪枝(上下界剪枝)。

2) 可行性剪枝

可行性剪枝一般是處理可行解的問題,如一個迷宮,問能否從起點到達目標點之類的。

舉個最簡單的例子,如圖7,問作者能否在正好第11秒的時候避過各種障礙物(圖中的東西一看就知道哪些是障礙物了,^_^)最終取得愛心,作者每秒能且只能移動一格,允許走重復的格子。

 夜深人靜寫算法(1):搜索入門

圖7

仔細分析可以發現,這是永遠不可能的,因為作者無論怎么走,都只能在第偶數秒的時候到達愛心的位置,這是他們的曼哈頓距離(兩點的XY坐標差的絕對值之和)的奇偶性決定的,所以這里我們可以在搜索的時候做奇偶性剪枝(可行性剪枝)。

類似的求可行解的問題還有很多,如N (N <= 25) 根長度不一的木棒,問能否選取其中幾根,拼出長度為K的木棒,具體就是枚舉取木棒的過程,每根木棒都有取或不取兩種狀態,所以總的狀態數為2^25,需要進行剪枝。用到的是剩余和不可達剪枝(隨便取的名字,即當前S根木棒取了S1根后,剩下的N-S根木棒的總和 加上 之前取的S1根木棒總和如果小于K,那么必然不滿足,沒必要繼續往下搜索),這個問題其實是個01背包,當N比較大的時候就是動態規劃了。

3) 最優性剪枝(上下界剪枝)

最優性剪枝一般是處理最優解的問題。以求兩個狀態之間的最小步數為例,搜索最小步數的過程:一般情況下,需要保存一個“當前最小步數”,這個最小步數就是當前解的一個下界d。在遍歷到搜索樹的葉子結點時,得到了一個新解,與保存的下界作比較,如果新解的步數更小,則令它成為新的下界。搜索結束后,所保存的解就是最小步數。而當我們已經搜索了k歩,如果能夠通過某種方式估算出當前狀態到目標狀態的理論最少步數s時,就可以計算出起點到目標點的理論最小步數,即估價函數h = k + s,那么當前情況下存在最優解的必要條件是h < d,否則就可以剪枝了。最優性剪枝是不斷優化解空間的過程。

4、基于DFS的A*(迭代加深,IDA*)

1) 算法原理

迭代加深分兩步走:
1、枚舉深度。
2、根據限定的深度進行DFS,并且利用估價函數進行剪枝。

2) 算法實現

迭代加深寫成偽代碼如下:

def IDA_Star(STATE startState):
    maxDepth = 0
    while true:
       if( DFS(startState, 0, maxDepth) ):
            return 
        maxDepth = maxDepth + 1

 夜深人靜寫算法(1):搜索入門

圖 8

3) 基礎應用

如圖8所示,一個“井”字形的玩具,上面有三種數字1、2、3,給出8種操作方式,A表示將第一個豎著的列循環上移一格,并且A和F是一個逆操作,B、C、D…的操作方式依此類推,初始狀態給定,目標狀態是中間8個數字相同。問最少的操作方式,并且要求給出操作的序列,步數一樣的時候選擇字典序最小的輸出。圖中的操作序列為AC。

大致分析一下,一共24個格子,每個格子三種情況,所以最壞情況狀態總數為3^24,但實際上,我們可以分三種情況討論,先確定中間的8個數字的值,假設為1的話,2和3就可以看成是一樣的,于是狀態數變成了2^24。

對三種情況分別進行迭代加深搜索,令當前需要搜索的中間8個數字為k,首先枚舉本次搜索的最大深度maxDepth(即需要的步數),從初始狀態進行狀態擴展,每次擴展8個結點,當搜索到深度為depth的時候,那么剩下可以移動的步數為maxDepth – depth,我們發現每次移動,中間的8個格子最多多一個k,所以如果當前狀態下中間8個格子有sum個k,那么需要的剩余步數的理想最小值s = 8 – sum,那么估價函數:

h = depth + (8 – sum)

當h > maxDepth時,表明在當前這種狀態下,不可能在maxDepth歩以內達成目標,直接回溯。

當某個深度maxDepth至少有一個可行解時,整個算法也就結束了,可以設定一個標記,直接回溯到最上層,或者在DFS的返回值給定,對于某個搜索樹,只要該子樹下有解就返回1,否則返回0。

迭代加深適合深度不是很深,但是每次擴展的結點數很多的搜索問題。

二、廣度優先搜索

1、BFS

1) 算法原理

廣度優先搜索即Breadth First Search,也是圖遍歷算法的一種。用一句話概括就是:“我會分身我怕誰?!”。
BFS的具體算法描述為選擇一個起始點v放入一個先進先出的隊列中,執行如下操作:
a. 如果隊列不為空,彈出一個隊列首元素,記為當前結點,執行b;否則算法結束;
b. 將與 當前結點 相鄰并且尚未被訪問的結點的信息進行更新,并且全部放入隊列中,繼續執行a;

維護廣搜的數據結構是隊列和HASH,隊列就是官方所說的open-close表,HASH主要是用來標記狀態的,比如某個狀態并不是一個整數,可能是一個字符串,就需要用字符串映射到一個整數,可以自己寫個散列HASH表,不建議用STL的map,效率奇低。

廣搜最基礎的應用是用來求圖的最短路。

如圖9所示,對以下圖進行廣度優先搜索,假設起點為1,將它放入隊列后。那么第一次從隊列中彈出的一定是1,將和1相鄰未被訪問的結點繼續按順序放入隊列中,分別是2、3、4、5、7,并且記錄下它們距離起點的距離dis[x] = dis[1] + 1 (x 屬于集合 {2, 3, 4, 5, 7});然后彈出的元素是2,和2相鄰未被訪問的結點是10,將它也放入隊列中,記錄dis[10] = dis[2] + 1;然后彈出5,放入6(4由于已經被訪問過,所以不需要再放入隊列中);彈出7,放入8、9。隊列為空后結束搜索,搜索完畢后,dis數組就記錄了起點1到各個點的最短距離;

 夜深人靜寫算法(1):搜索入門

圖9

2) 算法實現
廣搜一般用隊列維護狀態,寫成偽代碼如下:

def BFS(v):
    resetArray(visited,false)
    visited[v] = true
    queue.push(v)
    while not queue.empty():
        v = queue.getfront_and_pop()
        for u in adjcent_list[v]:
            if visited[u] is false:
                dosomething(u)
                queue.push(u)

3) 基礎應用

a. 最短路:bellman-ford最短路的優化算法SPFA,主體是利用BFS實現的。絕大部分四向、八向迷宮的最短路問題。
b. 拓撲排序:首先找入度為0的點入隊,彈出元素執行“減度”操作,繼續將減完度后入度為0的點入隊,循環操作,直到隊列為空,經典BFS操作;
c. FloodFill:經典洪水灌溉算法;

4) 高級應用

a. 差分約束:數形結合的經典算法,利用SPFA來求解不等式組。
b. 穩定婚姻:二分圖的穩定匹配問題,試問沒有穩定的婚姻,如何有心思學習算法,所以一定要學好BFS啊;
c. AC自動機:字典樹 + KMP + BFS,在設定失敗指針的時候需要用到BFS。詳細算法參見:http://www.cppblog.com/menjitianya/archive/2014/07/10/207604.html
d. 矩陣二分:矩陣乘法的狀態轉移圖的構建可以采用BFS;
e. 基于k進制的狀態壓縮搜索:這里的k一般為2的冪,狀態壓縮就是將原本多維的狀態壓縮到一個k進制的整數中,便于存儲在一個一維數組中,往往可以大大地節省空間,又由于k為2的冪,所以狀態轉移可以采用位運算進行加速,HDU1813HDU3278以及HDU3900都是很好的例子;
f. 其它:還有好多,一時間想不起來了,占坑;

2、基于BFS的A*

1) 算法原理
在搜索的時候,結點信息要用堆(優先隊列)維護大小,即能更快到達目標的結點優先彈出。

2) 基礎應用
a.八數碼問題
如圖10所示,一個3*3的棋盤,放置8個棋子,編號1-8,給定任意一個初始狀態,每次可以交換相鄰兩個棋子的位置,問最少經過多少次交換使棋盤有序。

 夜深人靜寫算法(1):搜索入門

圖10

遇到搜索問題一般都是先分析狀態,這題的狀態數可以這么考慮:將數字1放在九個格子中的任意一個,那么數字2有八種擺放方式,3有七種,依此類推;所以狀態總數為9的排列數,即9!(9的階乘) = 362880。每個狀態可以映射到0到362880-1的一個整數,
對于廣搜來說這個狀態量不算大,但是也不小,如果遇到無解的情況,就會把所有狀態搜遍,所以這里必須先將無解的情況進行特判,采用的是曼哈頓距離和逆序數進行剪枝,具體參見 SGU 139的解法:
http://www.cppblog.com/menjitianya/archive/2014/06/23/207386.html

網上對A*的描述寫的都很復雜,我嘗試用我的理解簡單描述一下,首先還是從公式入手:
f(state) = g(state) + h(state)

g(state):表示從初始狀態state 的實際行走步數,這個是通過BFS進行實時記錄的,是一個已知量;
h(state):表示從 state 目標狀態 的期望步數,這個是一個估計值,不能準確得到,只能通過一些方法估計出一個值,并不準確;
f(state):表示從 初始狀態目標狀態 的期望步數,這個沒什么好說的,就是前兩個數相加得到,也肯定是個估計值;

對于廣搜的狀態,我們是用隊列來維護的,所以state都是存在于隊列中的,我們希望隊列中狀態的f(state)值是單調不降的(這樣才能盡量早得搜到一個解),g(state)可以在狀態擴展的時候由當前狀態的父狀態pstate的g(pstate)+1得到;那么問題就在于h(state),用什么來作為state的期望步數,這個對于每個問題都是不一樣的,在八數碼問題中,我們可以這樣想:

這個棋盤上每個有數字的格子都住了一位老爺爺 (-_-|||),每位老爺爺都想回家,老爺爺的家就對應了目標狀態每個數字所在的位置,對于 i 號老爺爺,他要回家的話至少要走的路程為當前狀態state它在的格子pos[i] 和 目標狀態他的家target[i] 的曼哈頓距離。每位老爺爺都要回家,所以最少的回家距離就是所有的這些曼哈頓距離之和,這就是我們在state狀態要到達目標狀態的期望步數h(state),不理解請回到兩行前再讀一遍或者看下面的公式。

h(state) = sum( abs(pos[i].x – (i-1)/3) + abs(pos[i].y – (i-1)%3) ) (其中 1 <= i <= 8, 0 <= pos[i].x, pos[i].y < 3 )
最后附上原題鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1043

b.K短路問題
求初始結點到目標結點的第K短路,當K=1時,即最短路問題,K=2時,則為次短路問題,當K >= 3時需要A*求解。還是一個h(state)函數,這里可以采用state到目標結點的最短距離為期望距離;附上原題鏈接:http://poj.org/problem?id=2449

3、雙向廣搜

1) 算法原理

初始狀態 和 目標狀態 都知道,求初始狀態到目標狀態的最短距離;利用兩個隊列,初始化時初始狀態在1號隊列里,目標狀態在2號隊列里,并且記錄這兩個狀態的層次都為0,然后分別執行如下操作:

a.若1號隊列已空,則結束搜索,否則從1號隊列逐個彈出層次為K(K >= 0)的狀態;
i. 如果該狀態在2號隊列擴展狀態時已經擴展到過,那么最短距離為兩個隊列擴展狀態的層次加和,結束搜索;
ii. 否則和BFS一樣擴展狀態,放入1號隊列,直到隊列首元素的層次為K+1時執行b;

b.若2號隊列已空,則結束搜索,否則從2號隊列逐個彈出層次為K(K >= 0)的狀態;
i. 如果該狀態在1號隊列擴展狀態時已經擴展到過,那么最短距離為兩個隊列擴展狀態的層次加和,結束搜索;
ii. 否則和BFS一樣擴展狀態,放入2號隊列,直到隊列首元素的層次為K+1時執行a;

如圖11,S表示初始狀態,T表示目標狀態,紅色路徑連接的點為S擴展出來的,藍色路徑連接的點為T擴展出來的,當S擴展到第三層的時候發現有一個結點已經在T擴展出來的集合中,于是搜索結束,最短距離等于3 + 2 = 5;

 夜深人靜寫算法(1):搜索入門

圖11

雙廣的思想很簡單,自己寫上一兩個基本上就能總結出固定套路了,和BFS一樣屬于盲搜。

三、搜索題集整理
1、DFS
Red and Black ★☆☆☆☆ FloodFill
The Game ★☆☆☆☆ FloodFill
Frogger ★☆☆☆☆ 二分枚舉答案 + FloodFill
Nearest Common Ancestors ★☆☆☆☆ 最近公共祖先
Robot Motion ★☆☆☆☆ 遞歸模擬
Dessert ★☆☆☆☆ 枚舉
Matrix ★☆☆☆☆ 枚舉
Frame Stacking ★☆☆☆☆ 枚舉
Transportation ★☆☆☆☆ 枚舉
Pairs of Integers ★★☆☆☆ 枚舉
方程的解數 ★★☆☆☆ 枚舉 + 散列HASH
Maze ★★☆☆☆ 建完圖后FloodFill
Trees Made to Order ★★☆☆☆ 遞歸構造解
Cycles of Lanes ★★☆☆☆ 簡單圖最長環
The Settlers of Catan ★★☆☆☆ 簡單圖最長鏈
Parity game ★★☆☆☆ 簡單圖判奇環(交錯染色)
Increasing Sequences ★★☆☆☆ 枚舉
Necklace Decomposition ★★☆☆☆ 枚舉
Rikka with Tree ★★☆☆☆ 統計
Mahjong tree ★★★☆☆ 統計
Machine Schedule ★★★☆☆ 二分圖最大匹配
Chessboard ★★★☆☆ 棋盤覆蓋問題
Air Raid ★★★☆☆ DAG圖 最小路徑覆蓋
Entropy ★★★☆☆ 枚舉 + 適當剪枝
Dropping the stones ★★★☆☆ 枚舉 + 適當剪枝
Dreisam Equations ★★★☆☆ 表達式求值
Firefighters ★★★☆☆ 表達式求值
Cartesian Tree ★★★☆☆ 笛卡爾樹的構造
Binary Stirling Numbers ★★★☆☆ 分形
Obfuscation ★★★☆☆ 字符串匹配
Graph Coloring ★★★☆☆ 最大團
Pusher ★★★☆☆ 暴力搜索
Self-Replicating Numbers ★★★★☆ 枚舉
Last Digits ★★★★☆ DFS + 歐拉函數
Secret Code ★★★★☆ 實復數進制轉化
Anniversary Cake ★★★★☆ 矩形填充
A Puzzling Problem ★★★★☆ 枚舉擺放
Vase collection ★★★★☆ 圖的完美匹配
Packing Rectangles ★★★★☆ 枚舉擺放
Missing Piece 2001 ★★★★☆ N*N-1 數碼問題,強剪枝

2、IDA* (確定是迭代加深后就一個套路,枚舉深度,然后 暴力搜索+強剪枝)
Addition Chains ★★☆☆☆
DNA sequence ★★☆☆☆
Booksort ★★★☆☆
The Rotation Game ★★★☆☆ 迭代加深的公認經典題,理解“最少剩余步數”
Paint on a Wall ★★★☆☆ The Rotation Game 的簡單變形
Escape from Tetris ★★★★☆
Maze ★★★★☆
Rubik 2×2×2 ★★★★★ 編碼麻煩的魔方題
3、BFS
Pushing Boxes ★☆☆☆☆ 經典廣搜 – 推箱子
Jugs ★☆☆☆☆ 經典廣搜 – 倒水問題
Space Station Shielding ★☆☆☆☆ FloodFill
Knight Moves ★☆☆☆☆ 棋盤搜索
Knight Moves ★☆☆☆☆ 棋盤搜索
Eight ★★☆☆☆ 經典八數碼
Currency Exchange ★★☆☆☆ SPFA
The Postal Worker Rings ★★☆☆☆ SPFA
ROADS ★★☆☆☆ 優先隊列應用
Ones ★★☆☆☆ 同余搜索
Dogs ★★☆☆☆
Lode Runner ★★☆☆☆
Hike on a Graph ★★☆☆☆
Find The Multiple ★★☆☆☆ 同余搜索
Different Digits ★★★☆☆ 同余搜索
Magic Multiplying Machine ★★★☆☆ 同余搜索
Remainder ★★★☆☆ 同余搜索
Escape from Enemy Territory★★★☆☆ 二分答案 + BFS
Will Indiana Jones Get ★★★☆☆ 二分答案 + BFS
Fast Food ★★★☆☆ SPFA
Invitation Cards ★★★☆☆ SPFA
Galactic Import ★★★☆☆ SPFA
XYZZY ★★★☆☆ 最長路判環
Intervals ★★★☆☆ 差分約束
King ★★★☆☆ 差分約束
Integer Intervals ★★★☆☆ 差分約束
Sightseeing trip ★★★☆☆ 無向圖最小環
N-Credible Mazes ★★★☆☆ 多維空間搜索,散列HASH
Spreadsheet ★★★☆☆ 建立拓撲圖后廣搜
Frogger ★★★☆☆ 同余搜索
Ministry ★★★☆☆ 需要存路徑
Gap ★★★☆☆ A*
Maze ★★★☆☆ 二進制壓縮鑰匙的狀態
Hike on a Graph ★★★☆☆
All Discs Considered ★★★☆☆
Roads Scholar ★★★☆☆ SPFA
Holedox Moving ★★★☆☆
昂貴的聘禮 ★★★☆☆
Maze Stretching ★★★☆☆
Treasure of the Chimp ★★★☆☆
Is the Information Reliable ★★★☆☆ 最長路判環
It’s not a Bug, It’s a ★★★☆☆
Warcraft ★★★☆☆
Escape ★★★☆☆
Bloxorz I ★★★☆☆ 當年比較流行這個游戲
Up and Down ★★★★☆ 離散化 + BFS
Sightseeing ★★★★☆ SPFA
Cliff Climbing ★★★★☆ 日本人的題就是這么長
Cheesy Chess ★★★★☆ 仔細看題
The Treasure ★★★★☆
Chessman ★★★★★ 弄清狀態同余的概念
Puzzle ★★★★★ 幾乎嘗試了所有的搜索 -_-||| 讓人欲仙欲死的題
Unblock Me ★★★★★ 8進制壓縮狀態,散列HASH,位運算加速
4、雙向BFS(適用于起始狀態都給定的問題,一般一眼就能看出來,固定套路,很難有好的剪枝)
Solitaire ★★★☆☆
A Game on the Chessboard★★★☆☆
魔板 ★★★★☆
Tobo or not Tobo ★★★★☆
Eight II ★★★★★

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!