2024 day 23.
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
Mikaël Capelle 2024-12-23 11:35:42 +01:00
parent b9341bdecc
commit 77b24dd148
4 changed files with 3511 additions and 2 deletions

View File

@ -1,7 +1,36 @@
from collections import defaultdict
from typing import Any, Iterator from typing import Any, Iterator
from ..base import BaseSolver from ..base import BaseSolver
from ..tools.graphs import iter_max_cliques
class Solver(BaseSolver): class Solver(BaseSolver):
def solve(self, input: str) -> Iterator[Any]: ... def solve(self, input: str) -> Iterator[Any]:
connections: dict[str, set[str]] = defaultdict(set)
for row in input.splitlines():
src, dst = row.split("-")
connections[src].add(dst)
connections[dst].add(src)
if self.files:
content = "graph G {\n"
for row in input.splitlines():
src, dst = row.split("-")
content += f"{src} -- {dst}\n"
content += "}"
self.files.create("graph.dot", content.encode(), False)
cliques: set[frozenset[str]] = set()
for node1, neighbors in connections.items():
for node2 in neighbors:
for node3 in connections[node2].intersection(neighbors):
cliques.add(frozenset({node1, node2, node3}))
self.logger.info(f"found {len(cliques)} cliques of size 3")
yield sum(any(node.startswith("t") for node in clique) for clique in cliques)
# clique = max(nx.algorithms.clique.find_cliques(G), key=len)
clique = max(iter_max_cliques(connections), key=len)
yield ",".join(sorted(clique))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
kh-tc
qp-kh
de-cg
ka-co
yn-aq
qp-ub
cg-tb
vc-aq
tb-ka
wh-tc
yn-cg
kh-ub
ta-co
de-co
tc-td
tb-wq
wh-td
ta-ka
td-qp
aq-cg
wq-ub
ub-vc
de-ta
wq-aq
wq-vc
wh-yn
ka-de
kh-ta
co-tc
wh-qp
tb-vc
td-yn

View File

@ -1,5 +1,13 @@
import heapq import heapq
from typing import Callable, Iterable, TypeVar, overload from typing import (
Callable,
Iterable,
Iterator,
Mapping,
TypeVar,
cast,
overload,
)
_Node = TypeVar("_Node") _Node = TypeVar("_Node")
@ -116,3 +124,63 @@ def dijkstra(
return preds return preds
return preds.get(target, None) return preds.get(target, None)
def iter_max_cliques(
neighbors: Mapping[_Node, Iterable[_Node]], nodes: Iterable[_Node] | None = None
) -> Iterator[list[_Node]]:
"""
Find max cliques from the given set of neighbors containing the given set of nodes.
This is simply the networkx implementation with typing (and using a simple mapping
to avoid requiring networkx).
"""
if len(neighbors) == 0:
return
# remove the node itself from the neighbors
adj = {u: {v for v in neighbors[u] if v != u} for u in neighbors}
# Initialize Q with the given nodes and subg, cand with their nbrs
Q: list[_Node | None] = list(nodes or [])
cand = set(neighbors)
for node in Q:
if node not in cand:
raise ValueError(f"The given `nodes` {nodes} do not form a clique")
cand &= adj[node]
if not cand:
yield cast(list[_Node], Q[:])
return
subg = cand.copy()
stack: list[tuple[set[_Node], set[_Node], set[_Node]]] = []
Q.append(None)
u = max(subg, key=lambda u: len(cand & adj[u]))
ext_u = cand - adj[u]
try:
while True:
if ext_u:
q = ext_u.pop()
cand.remove(q)
Q[-1] = q
adj_q = adj[q]
subg_q = subg & adj_q
if not subg_q:
yield cast(list[_Node], Q[:])
else:
cand_q = cand & adj_q
if cand_q:
stack.append((subg, cand, ext_u))
Q.append(None)
subg = subg_q
cand = cand_q
u = max(subg, key=lambda u: len(cand & adj[u]))
ext_u = cand - adj[u]
else:
Q.pop()
subg, cand, ext_u = stack.pop()
except IndexError:
pass