00-两支队伍与奇怪的圈.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/env python3
  2. """课前零食:交替染色,并在冲突时给出一条奇环。"""
  3. from __future__ import annotations
  4. from collections import defaultdict, deque
  5. GOOD = [("A", "B"), ("A", "D"), ("C", "B"), ("C", "D"), ("E", "F")]
  6. BAD = [("A", "B"), ("B", "C"), ("C", "A"), ("C", "D")]
  7. def ask(prompt: str, default: str) -> str:
  8. try:
  9. return input(prompt).strip() or default
  10. except EOFError:
  11. return default
  12. def parse_edges(raw: str) -> list[tuple[str, str]]:
  13. result: list[tuple[str, str]] = []
  14. for token in raw.replace(",", ",").split(","):
  15. left, right = (part.strip() for part in token.split("-", 1))
  16. if not left or not right:
  17. raise ValueError("边应形如 A-B")
  18. result.append((left, right))
  19. return result
  20. def odd_cycle(u: str, v: str, parent: dict[str, str | None]) -> list[str]:
  21. up: list[str] = []
  22. cursor: str | None = u
  23. while cursor is not None:
  24. up.append(cursor)
  25. cursor = parent[cursor]
  26. vp: list[str] = []
  27. cursor = v
  28. while cursor is not None:
  29. vp.append(cursor)
  30. cursor = parent[cursor]
  31. position = {vertex: index for index, vertex in enumerate(up)}
  32. common = next(vertex for vertex in vp if vertex in position)
  33. left = up[:position[common] + 1]
  34. right = vp[:vp.index(common)]
  35. return left + list(reversed(right)) + [u]
  36. def color_graph(edges: list[tuple[str, str]]) -> None:
  37. graph: dict[str, list[str]] = defaultdict(list)
  38. for u, v in edges:
  39. graph[u].append(v)
  40. graph[v].append(u)
  41. color = {vertex: -1 for vertex in graph}
  42. parent: dict[str, str | None] = {}
  43. for start in sorted(graph):
  44. if color[start] != -1:
  45. continue
  46. color[start] = 0
  47. parent[start] = None
  48. queue: deque[str] = deque([start])
  49. print(f"\n从 {start} 染成 0 开始")
  50. while queue:
  51. u = queue.popleft()
  52. for v in graph[u]:
  53. if color[v] == -1:
  54. color[v] = 1 - color[u]
  55. parent[v] = u
  56. queue.append(v)
  57. print(f" 边 {u}-{v}:把 {v} 染成 {color[v]}")
  58. elif color[v] == color[u]:
  59. cycle = odd_cycle(u, v, parent)
  60. print(f" 冲突!{u} 与 {v} 都是颜色 {color[u]}。")
  61. print(" 一条奇环:", " -> ".join(cycle))
  62. return
  63. left = sorted(vertex for vertex, value in color.items() if value == 0)
  64. right = sorted(vertex for vertex, value in color.items() if value == 1)
  65. print("\n可二染:左侧(0)=", left, ";右侧(1)=", right)
  66. print("每个连通分量可以整体交换左右两侧。")
  67. def main() -> None:
  68. choice = ask("选择 g=可二染预设,b=奇环预设,c=自定义(回车为 g): ", "g").lower()
  69. if choice == "b":
  70. edges = BAD
  71. elif choice == "c":
  72. try:
  73. edges = parse_edges(ask("输入边 A-B,B-C: ", "A-B,B-C,C-A"))
  74. except (ValueError, IndexError) as error:
  75. print(f"格式不对:{error};改用奇环预设。")
  76. edges = BAD
  77. else:
  78. edges = GOOD
  79. print("边:", ", ".join(f"{u}-{v}" for u, v in edges))
  80. color_graph(edges)
  81. if __name__ == "__main__":
  82. main()