|
| 1 | +type ValueGraph = ReturnType<typeof parseFlowRateGraph>; |
| 2 | +type Distances = ReturnType<typeof computeDistances>; |
| 3 | + |
| 4 | +const regExp = |
| 5 | + /^Valve (?<id>.+) has flow rate=(?<flowRateText>\d+); tunnels? leads? to valves? (?<destinationsText>.+?)$/gm; |
| 6 | + |
| 7 | +function parseFlowRateGraph(text: string) { |
| 8 | + const flowRates = new Map<string, number>(); |
| 9 | + const tunnels = new Map<string, string[]>(); |
| 10 | + for (const { groups } of text.matchAll(regExp)) { |
| 11 | + const { id, flowRateText, destinationsText } = groups!; |
| 12 | + flowRates.set(id, +flowRateText); |
| 13 | + tunnels.set(id, destinationsText.split(", ")); |
| 14 | + } |
| 15 | + const nodes = () => flowRates.keys(); |
| 16 | + const value = (valve: string) => flowRates.get(valve); |
| 17 | + const connected = (valve: string) => tunnels.get(valve) ?? []; |
| 18 | + return { nodes, value, connected }; |
| 19 | +} |
| 20 | + |
| 21 | +function computeDistances(graph: ValueGraph) { |
| 22 | + const result = new Map<string, Map<string, number>>(); |
| 23 | + { |
| 24 | + for (const u of graph.nodes()) { |
| 25 | + const u2 = new Map<string, number>().set(u, 0); |
| 26 | + result.set(u, u2); |
| 27 | + for (const v of graph.connected(u)) u2.set(v, 1), u2.set(v, 1); |
| 28 | + } |
| 29 | + for (const m of graph.nodes()) { |
| 30 | + for (const u of graph.nodes()) { |
| 31 | + const u2 = result.get(u)!; |
| 32 | + const u2m = u2.get(m); |
| 33 | + if (u2m === undefined) continue; |
| 34 | + for (const v of graph.nodes()) { |
| 35 | + const m2v = result.get(m)?.get(v); |
| 36 | + if (m2v === undefined) continue; |
| 37 | + const u2m2v = u2m + m2v; |
| 38 | + const u2v = u2.get(v); |
| 39 | + if (u2v !== undefined && u2v < u2m2v) continue; |
| 40 | + u2.set(v, u2m2v); |
| 41 | + } |
| 42 | + } |
| 43 | + } |
| 44 | + } |
| 45 | + return result; |
| 46 | +} |
| 47 | + |
| 48 | +function findMaxPressure( |
| 49 | + graph: ValueGraph, |
| 50 | + distances: Distances, |
| 51 | + targets: Set<string>, |
| 52 | +) { |
| 53 | + let max = 0; |
| 54 | + const stack = [{ |
| 55 | + valve: "AA", |
| 56 | + t: 30, |
| 57 | + opened: new Set<string>(), |
| 58 | + released: 0, |
| 59 | + }]; |
| 60 | + while (stack.length) { |
| 61 | + const { valve, t, opened, released } = stack.pop()!; |
| 62 | + if (released > max) max = released; |
| 63 | + if (t === 0) continue; |
| 64 | + for (const destination of targets.difference(opened)) { |
| 65 | + const remaining = t - distances.get(valve)!.get(destination)! - 1; |
| 66 | + if (remaining < 0) continue; |
| 67 | + stack.push({ |
| 68 | + valve: destination, |
| 69 | + t: remaining, |
| 70 | + opened: new Set(opened).add(destination), |
| 71 | + released: released + remaining * graph.value(destination)!, |
| 72 | + }); |
| 73 | + } |
| 74 | + } |
| 75 | + return max; |
| 76 | +} |
| 77 | + |
| 78 | +export default function solve(input: string) { |
| 79 | + const flowRateGraph = parseFlowRateGraph(input); |
| 80 | + const distances = computeDistances(flowRateGraph); |
| 81 | + const targets = flowRateGraph.nodes() |
| 82 | + .filter((valve) => flowRateGraph.value(valve)) |
| 83 | + .reduce((set, value) => set.add(value), new Set<string>()); |
| 84 | + return findMaxPressure(flowRateGraph, distances, targets); |
| 85 | +} |
0 commit comments