00-从乱走到松弛.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #!/usr/bin/env python3
  2. """课前零食:比较 BFS、0-1 BFS 与 Dijkstra 的取点顺序。"""
  3. from __future__ import annotations
  4. from collections import deque
  5. import heapq
  6. EDGES = [
  7. ("S", "A", 1), ("S", "B", 0), ("B", "A", 0),
  8. ("A", "T", 1), ("B", "T", 1), ("S", "C", 1), ("C", "T", 1),
  9. ]
  10. def ask(prompt: str, default: str) -> str:
  11. try:
  12. return input(prompt).strip() or default
  13. except EOFError:
  14. return default
  15. def graph() -> dict[str, list[tuple[str, int]]]:
  16. result: dict[str, list[tuple[str, int]]] = {}
  17. for u, v, weight in EDGES:
  18. result.setdefault(u, []).append((v, weight))
  19. result.setdefault(v, [])
  20. return result
  21. def zero_one_bfs(adj: dict[str, list[tuple[str, int]]]) -> None:
  22. dist = {u: float("inf") for u in adj}
  23. dist["S"] = 0
  24. work: deque[str] = deque(["S"])
  25. print("\n--- 0-1 BFS ---")
  26. while work:
  27. u = work.popleft()
  28. print(f"取出 {u},dist={dist[u]},双端队列={list(work)}")
  29. for v, weight in adj[u]:
  30. candidate = dist[u] + weight
  31. if candidate < dist[v]:
  32. old = dist[v]
  33. dist[v] = candidate
  34. if weight == 0:
  35. work.appendleft(v)
  36. place = "队首(0 边)"
  37. else:
  38. work.append(v)
  39. place = "队尾(1 边)"
  40. print(f" 松弛 {u}->{v}({weight}):{old} -> {candidate},放{place}")
  41. print("最终距离:", dist)
  42. def dijkstra(adj: dict[str, list[tuple[str, int]]]) -> None:
  43. dist = {u: float("inf") for u in adj}
  44. dist["S"] = 0
  45. heap: list[tuple[int, str]] = [(0, "S")]
  46. print("\n--- Dijkstra ---")
  47. while heap:
  48. current, u = heapq.heappop(heap)
  49. if current != dist[u]:
  50. print(f"弹出旧条目 ({current}, {u}),当前已是 {dist[u]},跳过。")
  51. continue
  52. print(f"定案候选 {u},距离 {current}")
  53. for v, weight in adj[u]:
  54. candidate = current + weight
  55. if candidate < dist[v]:
  56. old = dist[v]
  57. dist[v] = candidate
  58. heapq.heappush(heap, (candidate, v))
  59. print(f" 松弛 {u}->{v}({weight}):{old} -> {candidate},推入堆")
  60. print("最终距离:", dist)
  61. def bfs_unit_edges(adj: dict[str, list[tuple[str, int]]]) -> None:
  62. dist = {u: -1 for u in adj}
  63. dist["S"] = 0
  64. work: deque[str] = deque(["S"])
  65. print("\n--- 把每条边都当 1 的 BFS(仅作对照)---")
  66. while work:
  67. u = work.popleft()
  68. for v, _ in adj[u]:
  69. if dist[v] == -1:
  70. dist[v] = dist[u] + 1
  71. work.append(v)
  72. print("按边数得到:", dist)
  73. print("它把 S->A 当作 1 步最短,却看不见 S->B->A 的总权为 0。")
  74. def main() -> None:
  75. print("预设有向边:", ", ".join(f"{u}->{v}({w})" for u, v, w in EDGES))
  76. choice = ask("选择 b=BFS 对照、z=0-1 BFS、d=Dijkstra(回车为 z): ", "z").lower()
  77. adj = graph()
  78. if choice == "b":
  79. bfs_unit_edges(adj)
  80. elif choice == "d":
  81. dijkstra(adj)
  82. else:
  83. zero_one_bfs(adj)
  84. if __name__ == "__main__":
  85. main()