Suppose you're trying to make goal 280
from sequence 11 6 16 20
. That is, you want to find some operators ⚀
, ⚁
, and ⚂
, so that 11 ⚀ 6 ⚁ 16 ⚂ 20 = 280
.
Consider using +
for the last operator ⚂
: 11 ⚀ 6 ⚁ 16 + 20 = 280
. Since we evaluate operators from left to right, this is equivalent to (11 ⚀ 6 ⚁ 16) + 20 = 280
. So we can only replace ⚂
with +
if we can make 11 ⚀ 6 ⚁ 16 = 280 - 20 = 260
. We should recursively try to make 260
from sequence 11 6 16
, and if we succeed, then we can also make 280
from 11 6 16 20
.
Note that we can skip the recursion unless 280 > 20
, because we don't get negative numbers or zero as input and we cannot use -
as an operator.
Also consider using *
for the last operator ⚂
: 11 ⚀ 6 ⚁ 16 * 20 = 280
. This is equivalent to (11 ⚀ 6 ⚁ 16) * 20 = 280
. So we can only replace ⚂
with *
if we can make 11 ⚀ 6 ⚁ 16 = 280 / 20 = 14
. We should recursively try to make 14
from 11 6 16
, and if we succeed, then we can also make 280
from 11 6 16 20
.
Note that we can skip the recursion unless 280
is a multiple of 20
because we don't get fractions as input and we cannot use ÷
(division) as an operator.
Turning this into Swift code, we get a function like this:
func canMake(_ goal: Int, from inputs: ArraySlice<Int>) -> Bool {
guard inputs.count > 1 else {
return goal == inputs.first!
}
var inputs = inputs
let last = inputs.removeLast()
if goal > last && canMake(goal - last, from: inputs) {
return true
}
if goal.isMultiple(of: last) && canMake(goal / last, from: inputs) {
return true
}
return false
}
// Example use:
if canMake(280, from: [11, 6, 16, 20]) {
answer += 280
}
For part 2, we get the new operator ||
. We can handle this very similarly to how we handled +
and *
, except now we need to look at the decimal representations of the goal and of the last number in the sequence. If we're trying to make 12963
and the last number in the sequence is 3
, then we can make 12963
as 1296 || 3
, so we recursively ask whether we can make 1296
using the earlier part of the sequence.
func canMake(_ goal: Int, from inputs: ArraySlice<Int>) -> Bool {
guard inputs.count > 1 else {
return goal == inputs.first!
}
var inputs = inputs
let last = inputs.removeLast()
if goal > last && canMake(goal - last, from: inputs) {
return true
}
if goal.isMultiple(of: last) && canMake(goal / last, from: inputs) {
return true
}
let goalDigits = String(goal)
let lastDigits = String(last)
if goalDigits.count > lastDigits.count && goalDigits.hasSuffix(lastDigits) {
let newGoal = Int(goalDigits.dropLast(lastDigits.count))!
return canMake(newGoal, from: inputs)
}
return false
}