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)