Skip to content

Commit 71a9aa8

Browse files
committed
Refactor Y2024D07.
Found better solution with more pruning heuristics.
1 parent 9bc05d2 commit 71a9aa8

File tree

1 file changed

+23
-14
lines changed

1 file changed

+23
-14
lines changed

Diff for: solutions/aockt/y2024/Y2024D07.kt

+23-14
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,17 @@ package aockt.y2024
33
import aockt.util.parse
44
import aockt.y2024.Y2024D07.Operator.*
55
import io.github.jadarma.aockt.core.Solution
6-
import kotlin.math.*
76

87
object Y2024D07 : Solution {
98

109
/** Possible operators for the calibration equations. */
11-
private enum class Operator {
12-
Add, Multiply, Concatenate;
10+
private enum class Operator { Add, Multiply, Concatenate }
1311

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()
1917
}
2018

2119
/** The numbers of a bridge repair calibration equation, with missing operators. */
@@ -28,14 +26,25 @@ object Y2024D07 : Solution {
2826
/** Determines if this equation has at least one solution using the given [operators]. */
2927
fun isSolvable(operators: Set<Operator>): Boolean {
3028

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) }
3645
}
3746

38-
return recurse(operands.first(), 0)
47+
return recurse(result, operands.lastIndex)
3948
}
4049
}
4150

0 commit comments

Comments
 (0)