|
| 1 | +package y25 |
| 2 | + |
| 3 | +import com.google.common.graph.Graph |
| 4 | +import com.google.common.graph.GraphBuilder |
| 5 | +import com.google.common.graph.Graphs |
| 6 | +import common.puzzle.solvePuzzle |
| 7 | +import common.puzzle.Input |
| 8 | +import common.puzzle.Puzzle |
| 9 | +import common.datastructures.* |
| 10 | +import common.ext.* |
| 11 | +import common.util.* |
| 12 | +import java.util.* |
| 13 | +import kotlin.math.* |
| 14 | +import kotlin.system.exitProcess |
| 15 | + |
| 16 | + |
| 17 | +fun main() = solvePuzzle(year = 2025, day = 11) { Day11(it) } |
| 18 | + |
| 19 | +@Suppress("UnstableApiUsage") |
| 20 | +class Day11(val input: Input) : Puzzle { |
| 21 | + |
| 22 | + private fun parseInput(): Graph<String> { |
| 23 | + val graph = GraphBuilder<String>.directed().build<String>() |
| 24 | + |
| 25 | + input.lines.forEach { line -> |
| 26 | + val (node, outputs) = line.split(": ") |
| 27 | + val outputNodes = outputs.split(" ") |
| 28 | + outputNodes.forEach { outputNode -> |
| 29 | + graph.putEdge(node, outputNode) |
| 30 | + } |
| 31 | + } |
| 32 | + |
| 33 | + return graph |
| 34 | + } |
| 35 | + |
| 36 | + data class Memoized( |
| 37 | + val node: String, |
| 38 | + val missingNodes: Set<String>, |
| 39 | + ) |
| 40 | + |
| 41 | + private fun Graph<String>.pathsToNode( |
| 42 | + from: String, |
| 43 | + target: String, |
| 44 | + requiredNodes: MutableSet<String>, |
| 45 | + visited: MutableSet<String> = mutableSetOf(), |
| 46 | + cache: MutableMap<Memoized, Long> = mutableMapOf(), |
| 47 | + ): Long { |
| 48 | + if (from == target) return if (requiredNodes.isEmpty()) 1 else 0 |
| 49 | + |
| 50 | + if (from in visited) { |
| 51 | + // Found cycle, ignore. |
| 52 | + return 0 |
| 53 | + } |
| 54 | + visited += from |
| 55 | + |
| 56 | + var addBack = false |
| 57 | + if (from in requiredNodes) { |
| 58 | + requiredNodes -= from |
| 59 | + addBack = true |
| 60 | + } |
| 61 | + |
| 62 | + val memo = Memoized(from, requiredNodes.toSet()) |
| 63 | + val result = cache[memo] ?: successors(from).sumOf { neigh -> |
| 64 | + pathsToNode(neigh, target, requiredNodes, visited, cache) |
| 65 | + }.also { cache[memo] = it } |
| 66 | + |
| 67 | + if (addBack) { |
| 68 | + requiredNodes += from |
| 69 | + } |
| 70 | + visited -= from |
| 71 | + |
| 72 | + return result |
| 73 | + } |
| 74 | + |
| 75 | + override fun solveLevel1(): Any { |
| 76 | + val graph = parseInput() |
| 77 | + return graph.pathsToNode("you", "out", mutableSetOf()) |
| 78 | + } |
| 79 | + |
| 80 | + override fun solveLevel2(): Any { |
| 81 | + val graph = parseInput() |
| 82 | + return graph.pathsToNode("svr", "out", mutableSetOf("dac", "fft")) |
| 83 | + } |
| 84 | +} |
0 commit comments