Advent of Code 2024 - Day 7Bridge Repair
| Problem statement | Source code | Tags: BFS/DFS
This problem pays tribute to 2022 day 9.
Part 1
Obviously this problem involves an exponential decision space: for every space, we can insert either a + or a *. An easy optimization is to stop whenever we find a valid expression, but when the expression actually doesn't work we are still forced to explore the whole space. In the input, the list of operands can have length up to 12, possibilities, which is not too bad.
However, we can prune edges that we know will never work. The trick is to construct the expression backwards.
When we try to fill in op1, we don't know anything and must try both + and *; but what about op[n-1]? If we want to fill it with *, then we know that b * an = target for some b, which means that target must be divisible by an. If it's not, then we can skip the * option for op[n-1]. The + option is always valid, so we can always try it. After filling in op[n-1], the problem has been reduced to one of the following:
And we repeat until we get a1 = target, which is trivial to check. The merit of this approach is that often times there remains only one valid option for the next operator, so we can skip a large portion of the search space, regardless of whether the final expression is valid or not. If a valid solution is found, it still returns early.
The order of the two tests don't matter, but I prefer to test the * option first since if it is true then that is more likely to lead to a solution, and we can skip the + option due to short circuiting.
Note that this takes operands right-to-left, so we need to reverse the list.
Part 2
The only difference is the addition of ||, which doesn't change our approach of searching backwards. Now we need to consider the reverse operation of ||: for *, we test if target / an is valid; for ||, we need to test if we can strip an from target as a string suffix, and still have some numbers left.