diff --git a/src/holt59/aoc/2015/day24.py b/src/holt59/aoc/2015/day24.py new file mode 100644 index 0000000..51462b0 --- /dev/null +++ b/src/holt59/aoc/2015/day24.py @@ -0,0 +1,88 @@ +from typing import Any, Iterator, TypeAlias + +from ..base import BaseSolver + +TupleOfInts: TypeAlias = tuple[int, ...] + + +def check_n_groups( + target: int, groups: tuple[TupleOfInts, ...], numbers: TupleOfInts +) -> bool: + n_groups = len(groups) + groups_s = tuple(sum(group) for group in groups) + + if all(target == group_s for group_s in groups_s): + return not numbers + + if not numbers: + return False + + head, *tail_l = numbers + tail, tail_s = tuple(tail_l), sum(tail_l) + + return any( + groups_s[i] + head <= target + and sum(groups_s[j] for j in range(len(groups)) if i != j) + tail_s + >= (n_groups - 1) * target + and check_n_groups( + target, groups[:i] + ((groups[i] + (head,)),) + groups[i + 1 :], tail + ) + for i in range(len(groups)) + ) + + +def enumerate_single_subset( + target: int, numbers: TupleOfInts +) -> Iterator[tuple[int, TupleOfInts, TupleOfInts]]: + """ + Enumerate subset of numbers whose sum equals target. + + Subset are enumerated in increasing order of length, then product (quantum value). + + Args: + target: Target for the sum of the subset. + numbers: Tuple of integers to find the subset from. + + Returns: + A generator (quantum, subset, remaining) where subset if the subset of numbers + whose sum equals target, quantum the product of the subset, and remaining the + remaining numbers. + """ + groups: list[tuple[int, TupleOfInts, TupleOfInts]] = [(1, (), numbers)] + + for _ in range(len(numbers)): + new_groups: list[tuple[int, TupleOfInts, TupleOfInts]] = [] + + for g_quantum, group, remaining in groups: + sg = sum(group) + for i in range(len(remaining)): + if group and remaining[i] <= group[-1]: + continue + + uv = remaining[:i] + remaining[i + 1 :] + kv = g_quantum * remaining[i], group + (remaining[i],), uv + + if sg + remaining[i] == target: + yield kv + elif sg + remaining[i] < target: + new_groups.append(kv) + + groups = new_groups + + +def find_min_quantum(numbers: tuple[int, ...], n_groups: int): + return next( + g_quantum + for g_quantum, group_1v2, group_234v2 in enumerate_single_subset( + sum(numbers) // n_groups, numbers + ) + if check_n_groups(sum(group_1v2), ((),) * (n_groups - 1), group_234v2) + ) + + +class Solver(BaseSolver): + def solve(self, input: str) -> Iterator[Any]: + numbers = tuple(map(int, input.split())) + + yield find_min_quantum(numbers, 3) + yield find_min_quantum(numbers, 4) diff --git a/src/holt59/aoc/inputs/holt59/2015/day24.txt b/src/holt59/aoc/inputs/holt59/2015/day24.txt new file mode 100644 index 0000000..874e61e --- /dev/null +++ b/src/holt59/aoc/inputs/holt59/2015/day24.txt @@ -0,0 +1,28 @@ +1 +3 +5 +11 +13 +17 +19 +23 +29 +31 +41 +43 +47 +53 +59 +61 +67 +71 +73 +79 +83 +89 +97 +101 +103 +107 +109 +113 diff --git a/src/holt59/aoc/inputs/tests/2015/day24.txt b/src/holt59/aoc/inputs/tests/2015/day24.txt new file mode 100644 index 0000000..f4909b4 --- /dev/null +++ b/src/holt59/aoc/inputs/tests/2015/day24.txt @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +7 +8 +9 +10 +11