diff --git a/2024/d23/ex2/ex2.py b/2024/d23/ex2/ex2.py new file mode 100755 index 0000000..fc50d4b --- /dev/null +++ b/2024/d23/ex2/ex2.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import sys +from collections import defaultdict +from collections.abc import Iterator + + +def solve(input: str) -> str: + def parse_line(input: str) -> tuple[str, str]: + lhs, rhs = input.split("-") + return lhs, rhs + + def parse(input: list[str]) -> list[tuple[str, str]]: + return [parse_line(line) for line in input] + + def links_to_graph(topology: list[tuple[str, str]]) -> dict[str, set[str]]: + graph: dict[str, set[str]] = defaultdict(set) + for lhs, rhs in topology: + graph[lhs].add(rhs) + graph[rhs].add(lhs) + return graph + + # Maximum clique solution thanks to [1] + # [1]: https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm + def bron_kerbosch(graph: dict[str, set[str]]) -> Iterator[set[str]]: + def helper( + clique: set[str], + candidates: set[str], + discarded: set[str], + ) -> Iterator[set[str]]: + if not candidates and not discarded: + yield clique + while candidates: + v = candidates.pop() + yield from helper( + clique | {v}, + candidates & graph[v], + discarded & graph[v], + ) + discarded.add(v) + + return helper(set(), set(graph.keys()), set()) + + topology = parse(input.splitlines()) + graph = links_to_graph(topology) + cliques = bron_kerbosch(graph) + historian_group = max(cliques, key=len) # MyPy doesn't like it inline in `sorted` + return ",".join(sorted(historian_group)) + + +def main() -> None: + input = sys.stdin.read() + print(solve(input)) + + +if __name__ == "__main__": + main()