11import static com .github .pareronia .aoc .IntegerSequence .Range .range ;
2- import static com .github .pareronia .aoc .Utils .toAString ;
3- import static com .github .pareronia .aoc .itertools .IterTools .combinations ;
42
5- import static java .util .stream .Collectors .joining ;
3+ import static java .util .stream .Collectors .toMap ;
64
7- import com .github .pareronia .aoc .IntegerSequence .Range ;
85import com .github .pareronia .aoc .Utils ;
6+ import com .github .pareronia .aoc .itertools .IterTools ;
97import com .github .pareronia .aoc .solution .Sample ;
108import com .github .pareronia .aoc .solution .Samples ;
119import com .github .pareronia .aoc .solution .SolutionBase ;
1210
13- import org .ojalgo .optimisation .Expression ;
14- import org .ojalgo .optimisation .ExpressionsBasedModel ;
15- import org .ojalgo .optimisation .Optimisation .Result ;
16- import org .ojalgo .optimisation .Variable ;
17-
1811import java .util .ArrayList ;
1912import java .util .Arrays ;
13+ import java .util .HashMap ;
2014import java .util .List ;
21- import java .util .function .ToLongFunction ;
15+ import java .util .Map ;
16+ import java .util .Map .Entry ;
2217
2318@ SuppressWarnings ({"PMD.ClassNamingConventions" , "PMD.NoPackage" })
24- public final class AoC2025_10 extends SolutionBase <List <AoC2025_10 .Machine >, Long , Long > {
19+ public final class AoC2025_10 extends SolutionBase <List <AoC2025_10 .Machine >, Integer , Integer > {
2520
2621 private AoC2025_10 (final boolean debug ) {
2722 super (debug );
@@ -37,63 +32,17 @@ public static AoC2025_10 createDebug() {
3732
3833 @ Override
3934 protected List <Machine > parseInput (final List <String > inputs ) {
40- return inputs .stream ().map (Machine ::fromInput ).toList ();
35+ return inputs .parallelStream ().map (Machine ::fromInput ).toList ();
4136 }
4237
4338 @ Override
44- public Long solvePart1 (final List <Machine > machines ) {
45- final ToLongFunction <Machine > buttonPressesForLights =
46- machine ->
47- Range .between (1 , machine .presses ().size ())
48- .intStream ()
49- .filter (
50- n ->
51- combinations (machine .presses ().size (), n ).stream ()
52- .anyMatch (machine ::pressesMatchLights ))
53- .findFirst ()
54- .orElseThrow ();
55- return machines .stream ().mapToLong (buttonPressesForLights ).sum ();
39+ public Integer solvePart1 (final List <Machine > machines ) {
40+ return machines .stream ().mapToInt (Machine ::buttonPressesForLights ).sum ();
5641 }
5742
5843 @ Override
59- @ SuppressWarnings ({
60- "PMD.AssignmentInOperand" ,
61- "PMD.AvoidInstantiatingObjectsInLoops" ,
62- "PMD.AvoidLiteralsInIfCondition"
63- })
64- public Long solvePart2 (final List <Machine > machines ) {
65- long ans = 0L ;
66- for (final Machine machine : machines ) {
67- final int vars = machine .presses ().size ();
68- final int targets = machine .joltages ().length ;
69- final double [][] a = new double [targets ][vars ];
70- range (vars ).stream ()
71- .forEach (
72- c -> Arrays .stream (machine .presses ().get (c )).forEach (p -> a [p ][c ] = 1 ));
73- final ExpressionsBasedModel model = new ExpressionsBasedModel ();
74- final List <Variable > variables =
75- range (vars ).stream ()
76- .map (c -> model .newVariable ("x" + c ).lower (0 ).integer ().weight (1 ))
77- .toList ();
78- range (targets ).stream ()
79- .forEach (
80- r -> {
81- final Expression constraint =
82- model .newExpression ("c" + r ).level (machine .joltages ()[r ]);
83- for (int c = 0 ; c < vars ; c ++) {
84- if (a [r ][c ] != 0d ) {
85- constraint .set (variables .get (c ), a [r ][c ]);
86- }
87- }
88- });
89- final Result result = model .minimise ();
90- ans +=
91- range (vars )
92- .intStream ()
93- .mapToLong (c -> Math .round (result .get (c ).doubleValue ()))
94- .sum ();
95- }
96- return ans ;
44+ public Integer solvePart2 (final List <Machine > machines ) {
45+ return machines .stream ().mapToInt (Machine ::buttonPressesForJoltages ).sum ();
9746 }
9847
9948 @ Samples ({
@@ -111,14 +60,21 @@ public static void main(final String[] args) throws Exception {
11160 [.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}
11261 """ ;
11362
114- record Machine (String lights , int [] joltages , List <int []> presses ) {
63+ private static List <Integer > parity (final List <Integer > lst ) {
64+ return lst .stream ().map (i -> i % 2 ).toList ();
65+ }
66+
67+ record Machine (
68+ List <Integer > lights ,
69+ List <Integer > joltages ,
70+ Map <List <Integer >, Map <List <Integer >, Integer >> patterns ) {
11571
11672 public static Machine fromInput (final String input ) {
11773 final String [] splits = input .split (" " );
118- final String lights =
74+ final List < Integer > lights =
11975 Utils .asCharacterStream (splits [0 ].substring (1 , splits [0 ].length () - 1 ))
120- .map (ch -> ( char ) ( '0' + ".#" . indexOf ( ch )) )
121- .collect ( toAString () );
76+ .map (ch -> ch == '#' ? 1 : 0 )
77+ .toList ( );
12278 final List <int []> presses = new ArrayList <>();
12379 for (int i = 1 ; i < splits .length - 1 ; i ++) {
12480 presses .add (
@@ -127,35 +83,88 @@ public static Machine fromInput(final String input) {
12783 .toArray ());
12884 }
12985 final String sj = splits [splits .length - 1 ];
130- final int [] joltages =
86+ final List < Integer > joltages =
13187 Arrays .stream (sj .substring (1 , sj .length () - 1 ).split ("," ))
132- .mapToInt (Integer ::parseInt )
133- .toArray ();
134- return new Machine (lights , joltages , presses );
88+ .map (Integer ::valueOf )
89+ .toList ();
90+ return new Machine (lights , joltages , createPatterns ( lights , joltages , presses ) );
13591 }
13692
137- private int pressAsBinary (final int idx ) {
138- return Arrays .stream (this .presses .get (idx ))
139- .map (i -> 1 << (this .lights .length () - 1 - i ))
140- .sum ();
93+ private static Map <List <Integer >, Map <List <Integer >, Integer >> createPatterns (
94+ final List <Integer > lights ,
95+ final List <Integer > joltages ,
96+ final List <int []> presses ) {
97+ final int numButtons = presses .size ();
98+ final int numVariables = joltages .size ();
99+ assert lights .size () == numVariables ;
100+ final Map <List <Integer >, Map <List <Integer >, Integer >> patterns =
101+ IterTools .product (List .of (0 , 1 ).iterator (), numVariables ).stream ()
102+ .collect (toMap (p -> p , p -> new HashMap <>()));
103+ for (int n = 0 ; n <= numButtons ; n ++) {
104+ final Iterable <int []> combos = IterTools .combinations (numButtons , n ).iterable ();
105+ for (final int [] combo : combos ) {
106+ final int [] pattern = new int [joltages .size ()];
107+ for (final int idx : combo ) {
108+ for (final int p : presses .get (idx )) {
109+ pattern [p ] += 1 ;
110+ }
111+ }
112+ final List <Integer > key = Arrays .stream (pattern ).boxed ().toList ();
113+ final List <Integer > parityPattern = parity (key );
114+ if (!patterns .get (parityPattern ).containsKey (key )) {
115+ patterns .get (parityPattern ).put (key , n );
116+ }
117+ }
118+ }
119+ return patterns ;
141120 }
142121
143- public boolean pressesMatchLights (final int ... idxs ) {
144- return Arrays .stream (idxs ).map (this ::pressAsBinary ).reduce (0 , (acc , c ) -> acc ^ c )
145- == Integer .parseInt (this .lights , 2 );
122+ public int buttonPressesForLights () {
123+ return this .patterns .get (this .lights ()).values ().stream ()
124+ .mapToInt (Integer ::intValue )
125+ .min ()
126+ .orElseThrow ();
146127 }
147128
148- @ Override
149- public String toString () {
150- final StringBuilder builder = new StringBuilder (100 );
151- builder .append ("Machine [lights=" )
152- .append (lights )
153- .append (", joltages=" )
154- .append (Arrays .toString (joltages ))
155- .append (", presses=[" )
156- .append (presses .stream ().map (Arrays ::toString ).collect (joining (", " )))
157- .append ("]]" );
158- return builder .toString ();
129+ public int buttonPressesForJoltages () {
130+ class DFS {
131+ private final Map <List <Integer >, Integer > cache ;
132+
133+ public DFS () {
134+ this .cache = new HashMap <>();
135+ }
136+
137+ public int dfs (final List <Integer > joltages ) {
138+ if (this .cache .containsKey (joltages )) {
139+ return this .cache .get (joltages );
140+ }
141+ int ans ;
142+ if (joltages .stream ().allMatch (j -> j == 0 )) {
143+ ans = 0 ;
144+ } else {
145+ ans = 10_000_000 ;
146+ final List <Integer > parityPattern = parity (joltages );
147+ for (final Entry <List <Integer >, Integer > e :
148+ Machine .this .patterns .get (parityPattern ).entrySet ()) {
149+ final List <Integer > pattern = e .getKey ();
150+ if (!range (pattern .size ())
151+ .intStream ()
152+ .allMatch (i -> pattern .get (i ) <= joltages .get (i ))) {
153+ continue ;
154+ }
155+ final List <Integer > newJoltages =
156+ range (pattern .size ()).stream ()
157+ .map (i -> (joltages .get (i ) - pattern .get (i )) / 2 )
158+ .toList ();
159+ ans = Math .min (ans , 2 * dfs (newJoltages ) + e .getValue ());
160+ }
161+ }
162+ this .cache .put (joltages , ans );
163+ return ans ;
164+ }
165+ }
166+
167+ return new DFS ().dfs (this .joltages ());
159168 }
160169 }
161170}
0 commit comments