↓快轉到主要內容
  1. Algorithms/

Shortest Path

·2 分鐘· ·
演算法 Graph Shortest-Path Dijkstra Bellman-Ford
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

介紹
#

Shortest path 題目是在 graph 裡找最低成本路徑。第一步要先判斷是哪一種路徑模型:

  • unweighted graph:BFS
  • non-negative weighted graph:Dijkstra
  • 有 edge-count limit 或需要處理負邊:Bellman-Ford 類 DP
  • grid 上的非加總成本:如果 path cost 單調,Dijkstra 仍然可用

Dijkstra
#

Dijkstra 適用在 path 延伸後成本不會變小的情況。一般 weighted graph 裡,path cost 是非負 edge weights 的總和。

from collections import defaultdict
from heapq import heappop, heappush
from typing import List

def dijkstra(n: int, edges: List[List[int]], src: int) -> List[float]:
    graph = defaultdict(list)
    for u, v, w in edges:
        graph[u].append((v, w))

    dist = [float("inf")] * n
    dist[src] = 0
    heap = [(0, src)]

    while heap:
        cost, node = heappop(heap)
        if cost != dist[node]:
            continue
        for nei, weight in graph[node]:
            new_cost = cost + weight
            if new_cost < dist[nei]:
                dist[nei] = new_cost
                heappush(heap, (new_cost, nei))

    return dist

Minimax Dijkstra
#

有些 graph 題的路徑成本不是總和。例如 path effort 是目前路徑上最大 edge effort:

new_effort = max(current_effort, edge_effort)

Dijkstra 仍然成立,因為這個成本是 monotonic:路徑延伸後不會降低已經付出的 effort。

Bellman-Ford With K Stops
#

當題目限制 edge 數量時,state 需要包含已使用的 flights/layers。k stops 代表最多可以用 k + 1 flights。

from typing import List

def cheapest_with_k_stops(n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:
    inf = float("inf")
    dist = [inf] * n
    dist[src] = 0

    for _ in range(k + 1):
        ndist = dist[:]
        for u, v, price in flights:
            if dist[u] != inf and dist[u] + price < ndist[v]:
                ndist[v] = dist[u] + price
        dist = ndist

    return -1 if dist[dst] == inf else dist[dst]

copy array 很重要:每一輪只能代表多使用一條 edge。

把限制放進狀態
#

限制不是距離本身時(例如最多轉 k 次彎),把它放進 state:(row, col, 上一步方向, 已轉彎數)。

pq = [(grid[0][0], 0, 0, 0, -1)]         # cost, turns, r, c, 上一步方向
best_turns = {}                          # (r, c, d) -> 處理過的最少轉彎數
while pq:
    cost, t, r, c, d = heapq.heappop(pq)
    if (r, c) == target:
        return cost
    if best_turns.get((r, c, d), k + 1) <= t:
        continue                         # 更便宜、轉彎又更少的已經處理過
    best_turns[(r, c, d)] = t
    for nd, (dr, dc) in enumerate(DIRS):
        nt = t + (d != -1 and nd != d)
        ...

同一個 (r, c, d) 先被 pop 的成本比較低;如果它的轉彎數也比較少,後面的狀態就被支配了,可以跳過(LC 4046)。

常見錯誤
#

  • weighted graph 還用普通 BFS。
  • 把答案說成 longest path,而不是 maximum shortest arrival time。
  • stops budget 是 state 的一部分,卻只用 city-only visited。
  • 題目要的是 path 上最大值,卻把 edge weight 加總。
  • Dijkstra 忘記處理 stale heap entries。
  • 運算子優先順序:turns + 1 if 轉彎 else 0 是整個式子二選一,直走時轉彎數被歸零(LC 4046)。

相關 LeetCode
#

  • LC 743 Network Delay Time
  • LC 778 Swim in Rising Water
  • LC 787 Cheapest Flights Within K Stops
  • LC 1631 Path With Minimum Effort
  • LC 4046 Minimum Cost Path With At Most K Turns

相關文章

LeetCode 499: The Maze III
·2 分鐘
LeetCode Hard Graph Dijkstra Shortest-Path Heap Matrix
LeetCode 499 解題筆記:Dijkstra 的優先度用 (距離, 路徑字串),滾動途中經過洞就要停下
LeetCode 743: Network Delay Time
·2 分鐘
LeetCode Medium Graph Dijkstra Shortest-Path
LeetCode 743 解題筆記,依照原始 learning note 重新整理
LeetCode 1631: Path With Minimum Effort
·2 分鐘
LeetCode Medium Graph Dijkstra Shortest-Path
LeetCode 1631 解題筆記,依照原始 learning note 重新整理
LeetCode 787: Cheapest Flights Within K Stops
·2 分鐘
LeetCode Medium Graph Bellman-Ford Shortest-Path
LeetCode 787 解題筆記,依照原始 learning note 重新整理
LeetCode 1514: Path with Maximum Probability
·1 分鐘
LeetCode Medium Graph Dijkstra Shortest-Path Heap
LeetCode 1514 解題筆記:機率相乘只會變小,所以 Dijkstra 可以直接用;剪枝要用 <=
Topological Sort
·2 分鐘
演算法 Graph Topological-Sort Kahn
排序、cycle detection、dependency unlocking 與 graph propagation