00-把合并史写成树.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #!/usr/bin/env python3
  2. """课前零食:把 Kruskal 的成功合并打印成一棵历史树。"""
  3. from __future__ import annotations
  4. EDGES = (
  5. (1, 2, 2),
  6. (3, 4, 3),
  7. (2, 3, 5),
  8. (4, 5, 6),
  9. (1, 5, 9),
  10. )
  11. class ReconstructionTree:
  12. def __init__(self) -> None:
  13. self.dsu_parent: dict[int, int] = {vertex: vertex for vertex in range(1, 6)}
  14. self.tree_parent: dict[int, int | None] = {vertex: None for vertex in range(1, 6)}
  15. self.children: dict[int, list[int]] = {vertex: [] for vertex in range(1, 6)}
  16. self.value: dict[int, int | None] = {vertex: None for vertex in range(1, 6)}
  17. self.logs: list[str] = []
  18. self.next_node = 6
  19. self.root = self.build()
  20. self.leaves: dict[int, list[int]] = {}
  21. self.fill_leaves(self.root)
  22. def find(self, node: int) -> int:
  23. while self.dsu_parent[node] != node:
  24. node = self.dsu_parent[node]
  25. return node
  26. def build(self) -> int:
  27. for u, v, weight in EDGES:
  28. a = self.find(u)
  29. b = self.find(v)
  30. if a == b:
  31. self.logs.append(f"边 ({u},{v},w={weight}):两端已在事件 {a} 下,跳过。")
  32. continue
  33. event = self.next_node
  34. self.next_node += 1
  35. self.children[event] = [a, b]
  36. self.value[event] = weight
  37. self.tree_parent[a] = event
  38. self.tree_parent[b] = event
  39. self.tree_parent[event] = None
  40. self.dsu_parent[a] = event
  41. self.dsu_parent[b] = event
  42. self.dsu_parent[event] = event
  43. self.logs.append(
  44. f"边 ({u},{v},w={weight}):合并根 {a} 与 {b},"
  45. f"新建事件 {event}@{weight}。"
  46. )
  47. return self.find(1)
  48. def fill_leaves(self, node: int) -> list[int]:
  49. if not self.children[node]:
  50. answer = [node]
  51. else:
  52. answer = []
  53. for child in self.children[node]:
  54. answer.extend(self.fill_leaves(child))
  55. self.leaves[node] = sorted(answer)
  56. return self.leaves[node]
  57. def ancestors(self, node: int) -> list[int]:
  58. chain = [node]
  59. while self.tree_parent[chain[-1]] is not None:
  60. chain.append(self.tree_parent[chain[-1]])
  61. return chain
  62. def lca(self, u: int, v: int) -> int:
  63. seen = set(self.ancestors(u))
  64. for node in self.ancestors(v):
  65. if node in seen:
  66. return node
  67. raise RuntimeError("同一棵重构树中一定有 LCA")
  68. def component_at(self, vertex: int, threshold: int) -> int:
  69. node = vertex
  70. while (
  71. self.tree_parent[node] is not None
  72. and self.value[self.tree_parent[node]] is not None
  73. and self.value[self.tree_parent[node]] <= threshold
  74. ):
  75. node = self.tree_parent[node]
  76. return node
  77. def print_tree(self, node: int | None = None, prefix: str = "", side: str = "根") -> None:
  78. if node is None:
  79. node = self.root
  80. children = self.children[node]
  81. if len(children) == 2:
  82. self.print_tree(children[1], prefix + " ", "右")
  83. label = str(node) if self.value[node] is None else f"{node}@{self.value[node]}"
  84. print(f"{prefix}{side}: {label} 覆盖叶子 {self.leaves[node]}")
  85. if len(children) == 2:
  86. self.print_tree(children[0], prefix + " ", "左")
  87. def show(self) -> None:
  88. print("\n成功合并的时间线:")
  89. for line in self.logs:
  90. print(" ", line)
  91. print("\n重构树(@ 后为发生权值):")
  92. self.print_tree()
  93. print("例如 LCA(2,4)=", self.lca(2, 4), ",权值=", self.value[self.lca(2, 4)])
  94. def warmup() -> None:
  95. print("=== 任务 0:先让并查集健忘一次 ===")
  96. print("边按权为 2、3、5、6、9 处理。")
  97. try:
  98. input("请预测:2 与 4 首次同一泡泡是在权值几?按回车: ")
  99. except EOFError:
  100. pass
  101. print("准备核对。注意:最终 DSU 只告诉你“五个人在一起”,不会保存这个答案。")
  102. def main() -> None:
  103. tree = ReconstructionTree()
  104. print("Kruskal 重构树小实验室:事件节点 6..9 是每次成功 union 的合影。")
  105. warmup()
  106. tree.show()
  107. print("\n命令:show;q 2 4;at 2 3;at 2 6;q。")
  108. while True:
  109. try:
  110. raw = input("\n> ").strip()
  111. except EOFError:
  112. raw = "q"
  113. if not raw or raw in {"q", "quit", "exit"}:
  114. return
  115. parts = raw.split()
  116. if parts == ["show"]:
  117. tree.show()
  118. elif len(parts) == 3 and parts[0] == "q":
  119. try:
  120. u, v = int(parts[1]), int(parts[2])
  121. if not (1 <= u <= 5 and 1 <= v <= 5):
  122. raise ValueError
  123. event = tree.lca(u, v)
  124. print(
  125. f"{u} 与 {v} 的最近共同事件是 {event}@{tree.value[event]};"
  126. f"首次连通阈值为 {tree.value[event]}。"
  127. )
  128. except ValueError:
  129. print("点编号应在 1..5。")
  130. elif len(parts) == 3 and parts[0] == "at":
  131. try:
  132. vertex, threshold = int(parts[1]), int(parts[2])
  133. if not 1 <= vertex <= 5:
  134. raise ValueError
  135. event = tree.component_at(vertex, threshold)
  136. print(
  137. f"阈值 {threshold} 时,{vertex} 上升到事件 {event}"
  138. f";所在块的原叶为 {tree.leaves[event]}。"
  139. )
  140. except ValueError:
  141. print("格式示例:at 2 5。")
  142. else:
  143. print("小提示:q 2 4 问首次连通;at 2 3 问某阈值时的块。")
  144. if __name__ == "__main__":
  145. main()