Advertisement
hhoppe

Advent of code 2024 day 18 part2 fastest Union-Find

Dec 18th, 2024 (edited)
144
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 1.88 KB | None | 0 0
  1. # import hhoppe_tools as hh  # For hh.UnionFind[tuple[int, int]].
  2.  
  3. # See https://en.wikipedia.org/wiki/Disjoint-set_data_structure.
  4. class UnionFind:
  5.  
  6.   def __init__(self):
  7.     self._rep = {}
  8.  
  9.   def union(self, a, b):
  10.     rep_a, rep_b = self.find(a), self.find(b)
  11.     self._rep[rep_b] = rep_a
  12.  
  13.   def same(self, a, b):
  14.     return self.find(a) == self.find(b)
  15.  
  16.   def find(self, a):
  17.     if a not in self._rep:
  18.       return a
  19.     parents = []
  20.     while True:
  21.       parent = self._rep.setdefault(a, a)
  22.       if parent == a:
  23.         break
  24.       parents.append(a)
  25.       a = parent
  26.     for p in parents:
  27.       self._rep[p] = a
  28.     return a
  29.  
  30.  
  31. # We detect if there an 8-connected path between anywhere on the N,E boundaries and anywhere on
  32. # the S,W boundaries.  If there is such a connected path, then we know that it prevents a
  33. # 4-connected path from (0, 0) to (size - 1, size - 1).
  34. # We iteratively consider each block yx, applying union_find.union(yx, yx2) with the special
  35. # labels nw_boundary and se_boundary if yx is boundary-adjacent and with all its 8-connected
  36. # neighboring blocks.
  37. # After adding each block, we see if the two boundary components are now in the same connected set.
  38. def day18_part2(s, *, size=71):
  39.   blocks = (map(int, row.split(',')) for row in s.splitlines())
  40.   union_find = UnionFind()
  41.   neighbors = set(itertools.product((-1, 0, 1), repeat=2)) - {(0, 0)}
  42.   seen = set()
  43.   nw_boundary, se_boundary = (-1, size), (size, -1)  # Arbitrary labels.
  44.   for x, y in blocks:
  45.     seen.add((y, x))
  46.     for dy, dx in neighbors:
  47.       y2, x2 = y + dy, x + dx
  48.       if y2 < 0 or x2 >= size:
  49.         union_find.union((y, x), nw_boundary)
  50.       elif x2 < 0 or y2 >= size:
  51.         union_find.union((y, x), se_boundary)
  52.       elif (y2, x2) in seen:
  53.         union_find.union((y, x), (y2, x2))
  54.     if union_find.same(nw_boundary, se_boundary):
  55.       return f'{x},{y}'
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement