🎯 什麼情境該想到我

當你「要一次算出所有點對之間的最短距離,而且點數不多()」的時候。
★ 它只有五行,是所有圖論演算法裡程式碼最短的——但那三層迴圈的順序只有一種是對的,而且寫錯不會報錯。

⚙️ 怎麼用(機制與推導)

核心:以「允許經過哪些中繼點」為階段的 DP

狀態定義:

轉移:考慮「要不要經過 」

邊界: 直接邊的權重(沒有邊就是 ,)。
答案:。

func FloydWarshall(d [][]int, n int) {
    // d[i][j] 已初始化為邊權,無邊為 INF,d[i][i] = 0
    for k := 0; k < n; k++ {          // ★ k 必須在最外層
        for i := 0; i < n; i++ {
            if d[i][k] == INF { continue }        // 小優化,也避免溢位
            for j := 0; j < n; j++ {
                if d[k][j] == INF { continue }
                if nd := d[i][k] + d[k][j]; nd < d[i][j] {
                    d[i][j] = nd
                }
            }
        }
    }
}

★ 為什麼 k 必須在最外層(這題唯一的考點)

k 是 DP 的「階段」,i、j 只是這一層要填的表格座標。

第 層要用的是第 層的完整結果。
k 放最外層,表示「在開始第 層之前,第 層的整張表已經全部算完」。

如果把 k 放到內層(例如 for i { for j { for k ),
你會在同一個 i 的表格還沒填完的時候就拿它去算別的格子——
用到的是「半成品」的狀態,結果會少算很多路徑。

★ 而且原地更新(只用一個二維陣列)是安全的:
第 層用到的 和 ,在第 層中不會被改變
(因為 ——經過 再到 沒有意義)。
所以不需要開第二個陣列。

⚠️ 順序寫錯的症狀:某些點對的距離偏大,但程式正常結束、沒有任何錯誤訊息。
這是典型的「安靜的錯」——所以要記住 k 在最外層。

複雜度

時間(無論邊多少)
空間

的實際上限:

可行性
100瞬間
500可接受(約數百毫秒)
1,000勉強(數秒),記憶體 8 MB
5,000❌ 不可行;記憶體也要 200 MB

★ 的記憶體常常比 的時間更早成為瓶頸。

和其他做法的選擇

情況用什麼
小(≤ 500)、要全對★ Floyd-Warshall —— 程式碼最短、常數極小、快取友善(連續二維陣列)
稀疏大圖、要全對、無負權跑 次 Dijkstra 最短路徑:
稀疏大圖、有負權Johnson:一次 Bellman-Ford 最短路徑 做重新賦權消掉負權,再跑 次 Dijkstra
只要單源Dijkstra 或 Bellman-Ford

★ 三個好用的變體(同一個框架換個運算)

變體把 min(+) 換成得到什麼
傳遞閉包or(and) → d[i][j] |= d[i][k] && d[k][j]i 到不到得了 j(Warshall 演算法)。可改用 bitset 加速到
最小瓶頸路min(max) → d[i][j] = min(d[i][j], max(d[i][k], d[k][j]))路徑上最大邊權的最小值(瓶頸頻寬、最低承載)
最大可靠度max(×)路徑上機率乘積的最大值

★ 這是一個很漂亮的抽象:把 換成任何滿足「半環」性質的運算對,演算法就照樣成立。

負權與負環

  • 允許負權邊 ✅
  • 偵測負環:跑完後檢查 d[i][i] < 0 —— 有任何一個對角線變成負數就代表 在某個負環上
  • 有負環時其他格子的值無意義

路徑還原

另開 next[i][j] 記錄「從 i 往 j 走的第一步」,在更新時 next[i][j] = next[i][k],之後逐步跳即可還原完整路徑。

🧪 我實際套用的紀錄

  • (待填)

⚠️ 注意 / 什麼時候不適用

  • ★ k 一定要在最外層。寫錯不會報錯,只會安靜地算出偏大的距離。
  • 基本不可行(時間 、記憶體 )。
  • INF 要用 math.MaxInt/2 或做檢查,否則 INF + INF 溢位變負數,會產生荒謬的「捷徑」。
  • 不要為了「只要幾對」而跑全對。只要少數幾個源點就跑幾次 Dijkstra。
  • 稀疏圖上非常浪費: 時,Floyd 仍然要跑 ,而 次 Dijkstra 只要 。
  • 初始化容易出錯:對角線要設 0、重邊要取最小、有向與無向的初始化不同。
  • 快取友善是它的隱藏優勢:連續的二維陣列 + 規律的存取模式,實測常數比理論分析看起來好得多——所以 時它往往比「跑 500 次 Dijkstra」更快。

🔗 相關工具