不熟的話,底下介紹的都看不懂了
圖論最為重要觀念就是 "連通",
若說此圖為連通圖,則所有點能透過邊接到任一點 (弱連通)[1]。
而不只接上還能走過去(有向邊)[2],則為強連通。
例如:
上圖弱連通但非強連通。
若是這樣:
則上圖為強連通。
以下兩個結構體,將成為本篇的慣例
討論圖的邊,常會有 是邊起點與 是邊終點的慣例用符
最小生成樹 (Minimum spanning tree) 簡稱 MST。
給定圖 ,使用 子集連結所有點 (來自 ) 所得到的樹為生成樹。
為邊集合, 為點集合
若帶權重圖的某生成樹為所有生成樹中權重總和最小,則稱此為最小生成樹
[3]
給定一個連通圖,找出一個最小生成樹
Kruskal 用了兩個重要的觀念:
這有什麼可利用的呢?
若將一點連向另一點,整張圖就少了一個連通塊,
反覆操作,圖最終只剩一個連通塊,若最終連通塊沒有環,那他就是樹,且為此圖的生成樹。
於是在連通塊與連通塊相連這個動作,確保產生的連通塊無環就行:
連通塊 想與連通塊 相連,只要 ,那麼相連的新連通塊保證無環。
怎樣產生出最小生成樹?
所以若在 與 相連時每次考慮最小權重的邊,那最終產生的一定是最小權重的生成樹
沒錯哦,這是最優子結構
實作上先將每個邊依照權重排序,
接著用 DFS 判斷塊與塊是否為同個連通塊,若不是的話就能相連起來。
DFS 每次都將連通塊的邊遍歷,複雜度為
還記得 Union-Find Forest 嗎?分類問題包括連通塊的分類也能使用它:
複雜度約為
其中 為 Union-Find Forest 的時間成本 (通常很小)
很直覺的,使 outpost 都能通訊的簡單方法,就是做出樹
畢竟若兩點已能通訊,加入一條形成環的邊不會更好。
而想使 D 下降的方法就是將 network 上的 distance 盡量小,也就是要求最小生成樹
而生成樹上較大的邊就用 satellite channel 來解決
先把 outpost 之間的距離算出來:
接著將最小生成樹 (network) 上最大的距離找出來:
TIOJ 1211 圖論 之 最小生成樹
AIZU 1280 Slim Span
NCPC 2018 Final Problem E Connecting Cities
TIOJ 1445 機器人組裝大賽
TIOJ 1795 咕嚕咕嚕呱啦呱啦
Prim 維護一個未完成的生成樹
其精神是:每次將樹周遭有最小權重的邊接到樹上,使樹最終成長至最小生成樹
傑出的一手貪心策略
Prim 用了兩個重要的觀念:
跟 Kruskal 一樣呢
首先隨便找任意點,作為那個初始的"未完成的生成樹",也就是一塊無環的連通圖
每次將 能觸及到的最小權重的邊接上 ,那最終產生的一定是最小權重的生成樹
跟 Kruskal 不同的是,這裡枚舉的是"點"
複雜度約
UVa OJ 908 Re-connecting Computer Sites
ACM-ICPC 2005 Southwestern 3505 Buy or Build
是路徑搜尋演算法的一個抽象觀點
A* 在節點 有個評估函數
當 大於門檻時就剪枝
表示從起點到節點 的成本
表示從節點 到終點的成本
評估函數是根據欲求解問題而設計,討論上是相當自由的
例如:
IDA* 是種 A* 的變形,會逐步地將門檻變寬
假設門檻是遞迴深度 (maxd
),那麼就逐漸放寬標準:
進行搜尋時一旦超過門檻就結束搜尋:
直覺的,將所有羃次 (p
) 的組合全部都試試看,找出最小的操作數 (operations) 就行:
執行前先設定邊界(當還未進行操作):
複雜度為
複雜度頗高, 的時候就能感受到輸出遲緩了
解答也許在深度還很小的時候就已經能找到了,所以將深度給定一個上限 (ops
):
接著每次將深度/操作數 (ops
) 的上限逐步提高:
但這題還不只如此,除了 IDA* 門檻設定以外,還得額外剪枝
當剩餘的操作數絕不可能湊出目標羃次 (n
) 時,就不繼續以此狀態深入搜尋:
UVa OJ 11212 Editing a Book
UVa OJ 12558 Egyptian Fractions (HARD version)
UVa OJ 1343 The Rotation Game
UVa OJ 11846 Finding Seats Again
* UVa OJ 11376 Tilt!
單源最短路徑 (Single-Source Shortest Paths) 簡稱 SSSP。
源點 (Source),就是起點,通常問題都只是簡單求每條路徑的最小成本。
求單源點到其他所有節點的最短路徑總權重
想像自己在圖中是某一點 v
,你看到你的鄰點們 u
,他們告訴你:源點走到他們那裡的最小花費成本 cost[u]
是多少,同時你也知道那些點們要過來你這分別要花費多少成本 w
(i.e. 邊權重)
考慮源點到自己這的最小成本 cost[v]
得知:
狀態 代表從源點到 的最小成本
對於點 顯然若 ,則
所以狀態轉移方程為
這就是 relaxation
邊界(源點到源點最小成本)為
初始其餘的狀態設為無限大[6],以表示尚未得到最佳解
若是一次將所有邊都做 relaxation,那麼重複做,每次至少可以將一個點解出
在下方 Dijkstra's algorithm 有給出間接證明
最開始先從邊界(起點)的鄰點解出(relaxation),接著是鄰點們的鄰點.. 直至所有的點。
所以至多只要重複做 次,就能將所有點解出
是因為起點已經先被解出了
但假設 是最優解,而 被 更新了, 還不一定是最優解
找單源最短路徑就是這麼簡單
Image Not Showing Possible ReasonsLearn More →
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
與第七週教材比較,兩者不同在於狀態求解的順序
Bellman-Ford 演算法可以簡單處理權重是負的情況:
先做 次的每邊 relaxation
接著就能判斷是否能回到過去
POJ 2387 Til the Cows Come Home
UVa OJ 10000 Longest Paths
Bellman-Ford’s algorithm 的進化版本,
其動機是: 若邊被 relax 過,則它的鄰點可以直接進行 relaxation
長得很像 Dijkstra’s algorithm
最壞複雜度
平均而言 , SPFA 提出者認為 很小[7]
UVa OJ 658 It’s not a Bug, it’s a Feature!
Dijkstra 演算法可看作是 BFS 的帶權重版本
BFS (Breadth-first Search) 的詳細流程請複習第五週教材
再次從 Bellman-Ford 演算法出發,
但每次欲更新 需從已解的 做 relaxation,其中
考慮 Bellman-Ford 演算法不斷更新 直至得最優解
每一次對所有邊都嘗試 relaxation 會有一些點被更新,姑且稱剛被更新的點為候選點
候選點都由已解的點 relax 過了,接下來只剩他們可以互相 relax 彼此
但值最小的點不會再被 relax 了
不被 relax 的條件是 ,這裡 是候選點
是因 且 不為負。此刻 就是最優解
籠統地說,是找不到點能靠加正值去更新比自己小的點
接著找出源點到任意點的最短路徑,其就是 BFS 並搭配 Relaxation
而 queue 將保存候選點,且負責挑出最小的點 (已解狀態)
其複雜度為
演算法的終止條件是 Q.empty() == true
,
也就是說,若有個邊權重為負的,則 Q.push({v.id, update});
將無盡的執行下去。
Bellman-Ford 演算法會提供解決之道
對於點 v
每次將比最短路 d1[v]
還大的更新值 ud
交給次短路 d2[v]
去做 relaxation
NCKU OJ 10 探望麻衣
UVa OJ 10986 Sending email
UVa OJ 10801 Lift Hopping
全點對最短路徑 (All-Pairs Shortest Paths) 簡稱 APSP。
顧名思義,可以得知所有點與點之間的最短路徑,同樣的一般只需求最小花費成本而已。
設定狀態 為 到 只許以 為中間點的最小總成本
而 到 間
所以狀態轉移方程:
邊界:
經由滾動陣列優化方法,可使狀態三維降至二維:
同學自行研究,這很簡單的
題目不是求最短路,而是求路徑數,但只需依樣畫葫蘆就行了!
若已知 有 種路, 有 種路,則根據乘法原理[9] 至少有 種路:
而根據題目,兩點之間的路徑中若有環,則兩點之間有無限多種路徑
若有環則表示某個點 可透過一些路走回點 ;換句話說,存在點 ,其中 有一些路、 也有一些路,所以根據前面路徑數的求法, 就會有一些路產生:
TIOJ 1096 E.漢米頓的麻煩
Google Code Jam Kickstart Round F 2018 B Specializing Villages: Small dataset
UVa OJ 11838 Come and Go
UVa OJ 10724 Road Construction
Google Code Jam Round 1B 2017 C Pony Express
把有向邊當成無向邊後,若連通的話就稱為弱連通 ↩︎
只看兩鄰點 與 , 表示這之間只有一條邊而 能走過去到 ; 而 不能走到 ↩︎
Wikipedia/ A demo for Kruskal's algorithm based on Euclidean distance. ↩︎
Wikipedia/ A demo for Prim's algorithm based on Euclidean distance. ↩︎
也能設為其他具代表性的數字作為未知的總成本 ↩︎
主編個人覺得 Dijkstra’s algorithm 實用期望值比較高 ↩︎