00-只修够用的路.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. #!/usr/bin/env python3
  2. """课前零食:追踪 Kruskal 和并查集的每一次合并。"""
  3. from __future__ import annotations
  4. DEFAULT = [
  5. ("A", "B", 1), ("D", "E", 1), ("A", "C", 2), ("C", "D", 2),
  6. ("B", "C", 3), ("B", "D", 4), ("C", "E", 5),
  7. ]
  8. def ask(prompt: str, default: str = "") -> str:
  9. try:
  10. return input(prompt).strip() or default
  11. except EOFError:
  12. return default
  13. def parse_edges(raw: str) -> list[tuple[str, str, int]]:
  14. result: list[tuple[str, str, int]] = []
  15. for token in raw.replace(",", ",").split(","):
  16. parts = [part.strip() for part in token.split("-")]
  17. if len(parts) != 3 or not parts[0] or not parts[1]:
  18. raise ValueError("每条边应形如 A-B-3")
  19. result.append((parts[0], parts[1], int(parts[2])))
  20. return result
  21. class DSU:
  22. def __init__(self, vertices: set[str]) -> None:
  23. self.parent = {vertex: vertex for vertex in vertices}
  24. self.size = {vertex: 1 for vertex in vertices}
  25. def find(self, vertex: str) -> str:
  26. if self.parent[vertex] != vertex:
  27. self.parent[vertex] = self.find(self.parent[vertex])
  28. return self.parent[vertex]
  29. def union(self, left: str, right: str) -> bool:
  30. left, right = self.find(left), self.find(right)
  31. if left == right:
  32. return False
  33. if self.size[left] < self.size[right]:
  34. left, right = right, left
  35. self.parent[right] = left
  36. self.size[left] += self.size[right]
  37. return True
  38. def groups(self) -> list[list[str]]:
  39. result: dict[str, list[str]] = {}
  40. for vertex in self.parent:
  41. result.setdefault(self.find(vertex), []).append(vertex)
  42. return [sorted(group) for group in result.values()]
  43. def main() -> None:
  44. raw = ask("边表 A-B-3,B-C-1(回车用预设): ")
  45. try:
  46. edges = DEFAULT if not raw else parse_edges(raw)
  47. except (ValueError, TypeError) as error:
  48. print(f"输入格式有问题:{error};使用预设。")
  49. edges = DEFAULT
  50. vertices = {vertex for edge in edges for vertex in edge[:2]}
  51. dsu = DSU(vertices)
  52. chosen: list[tuple[str, str, int]] = []
  53. total = 0
  54. print("\n--- 按权排序后 ---")
  55. for u, v, weight in sorted(edges, key=lambda edge: edge[2]):
  56. before = (dsu.find(u), dsu.find(v))
  57. if dsu.union(u, v):
  58. chosen.append((u, v, weight))
  59. total += weight
  60. print(f"选 {u}-{v} ({weight}):分量 {before[0]} 与 {before[1]} 合并 -> {dsu.groups()}")
  61. else:
  62. print(f"跳过 {u}-{v} ({weight}):两端都在分量 {before[0]},会闭合环")
  63. print("\n选中的边:", chosen)
  64. if len(chosen) == len(vertices) - 1:
  65. print(f"MST 总权={total},恰好选了 {len(vertices) - 1} 条边。")
  66. else:
  67. print("图不连通,只得到最小生成森林:", dsu.groups())
  68. if __name__ == "__main__":
  69. main()