@@ -3,19 +3,17 @@ package aockt.y2024
3
3
import aockt.util.parse
4
4
import aockt.y2024.Y2024D07.Operator.*
5
5
import io.github.jadarma.aockt.core.Solution
6
- import kotlin.math.*
7
6
8
7
object Y2024D07 : Solution {
9
8
10
9
/* * Possible operators for the calibration equations. */
11
- private enum class Operator {
12
- Add , Multiply , Concatenate ;
10
+ private enum class Operator { Add , Multiply , Concatenate }
13
11
14
- fun eval ( a : Long , b : Long ): Long = when ( this ) {
15
- Add -> a + b
16
- Multiply -> a * b
17
- Concatenate -> 10.0 .pow(floor(log10(b.toDouble()) + 1 )).toLong() * a + b // Optimised "$a$b".toLong()
18
- }
12
+ /* * Undoes an operator by applying the opposite. Unsafe calls throw. */
13
+ private fun Operator. undo ( a : Long , b : Long ): Long = when ( this ) {
14
+ Add -> a - b
15
+ Multiply -> a / b
16
+ Concatenate -> a.toString().removeSuffix(b.toString()).toLong()
19
17
}
20
18
21
19
/* * The numbers of a bridge repair calibration equation, with missing operators. */
@@ -28,14 +26,25 @@ object Y2024D07 : Solution {
28
26
/* * Determines if this equation has at least one solution using the given [operators]. */
29
27
fun isSolvable (operators : Set <Operator >): Boolean {
30
28
31
- // DFS + pruning: Since all operators result in larger numbers, quit early if overshooting the result.
32
- fun recurse (acc : Long , index : Int ): Boolean = when {
33
- acc > result -> false
34
- index + 1 >= operands.size -> acc == result
35
- else -> operators.any { recurse(it.eval(acc, operands[index + 1 ]), index + 1 ) }
29
+ // DFS + pruning: Solve equation backwards, removing candidate operators that can't apply:
30
+ // - Can't be Add if subtracting would lead to negative numbers.
31
+ // - Can't be Multiply if division is not exact, or divisor is zero.
32
+ // - Can't be Concatenate if the number isn't a (strictly shorter) suffix of the other.
33
+ // When reaching the last number, the equation succeeds if it is equal to the accumulator.
34
+ fun recurse (acc : Long , index : Int ): Boolean {
35
+ if (index == 0 ) return acc == operands.first()
36
+ val number = operands[index]
37
+ return operators
38
+ .asSequence()
39
+ .filterNot { it == Add && acc < number }
40
+ .filterNot { it == Multiply && number == 0L }
41
+ .filterNot { it == Multiply && acc % number != 0L }
42
+ .filterNot { it == Concatenate && acc == number }
43
+ .filterNot { it == Concatenate && ! acc.toString().endsWith(number.toString())}
44
+ .any { recurse(it.undo(acc, number), index - 1 ) }
36
45
}
37
46
38
- return recurse(operands.first(), 0 )
47
+ return recurse(result, operands.lastIndex )
39
48
}
40
49
}
41
50
0 commit comments