A clear explanation of expressing a target using the fewest operators with repeated uses of x.
Problem Restatement
We are given two integers:
x
targetWe may build expressions using only the number x and the operators:
+
-
*
/Division is real division, and standard operator precedence applies.
We cannot use unary negation. So:
-xis not allowed directly.
We want to express target using the minimum number of operators.
Return that minimum number.
The official constraints are:
| Constraint | Value |
|---|---|
2 <= x <= 100 | Base number |
1 <= target <= 2 * 10^8 | Desired value |
Source: LeetCode problem statement. (leetcode.com)
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer x |
| Input | Integer target |
| Output | Minimum number of operators needed |
| Allowed numbers | Only repeated uses of x |
Example function shape:
def leastOpsExpressTarget(x: int, target: int) -> int:
...Examples
Example 1:
x = 3
target = 19Output:
5One optimal expression is:
3 * 3 + 3 * 3 + 3 / 3This equals:
9 + 9 + 1 = 19Operator count:
*
+
*
+
/Total:
5Example 2:
x = 5
target = 501Output:
8Example 3:
x = 100
target = 100000000Output:
3Expression:
100 * 100 * 100 * 100This uses three multiplication operators.
First Thought: Build Expressions Directly
We could try generating expressions recursively.
At each step:
- multiply by
x - divide by
x - add another term
- subtract another term
But the number of expressions grows explosively.
We need a mathematical structure.
Key Insight
The expression behaves like writing the target in base x.
Example:
x = 3
target = 19Base 3 representation:
19 = 2 * 9 + 0 * 3 + 1That means:
19 = 2 * (3^2) + 1The powers of x are important:
x^0 = 1
x^1 = x
x^2 = x * x
x^3 = x * x * xEach power has a cost.
To build:
x^kwe need:
kmultiplication operators.
But there is one special case:
1 = x / xwhich costs one division operator.
So:
| Value | Cost |
|---|---|
x^0 = 1 | 2 operands but 1 operator |
x^k | k operators |
Recursive Choice
Suppose:
target = q * x + rwhere:
0 <= r < xWe have two choices.
Choice 1: Build Upward
Use:
r copiesof the lower power.
Example:
target = 19
x = 3
19 = 6 * 3 + 1We recursively build:
6then add one more unit.
Choice 2: Overshoot Then Subtract
Instead of using r, use:
x - rand subtract from the next higher multiple.
Example:
19 = 7 * 3 - 2Sometimes subtracting fewer pieces is cheaper than adding many pieces.
So we take the minimum of the two strategies.
Dynamic Programming Interpretation
Define:
dfs(t)as the minimum operator cost to build t.
We recursively divide by x:
q, r = divmod(t, x)Then compare:
build q and add r copiesversus:
build q + 1 and subtract x - r copiesMemoization avoids recomputing states.
Base Cases
If:
t == 0then no operators are needed.
If:
t < xthen there are two ways to build t.
Add Small Units
Build:
t * (x / x)Cost:
2 * t - 1because each 1 costs division, and additions connect them.
Subtract From x
Build:
x - (x - t)Cost:
2 * (x - t)We choose the cheaper one.
Correctness
We prove that the recursive relation computes the minimum number of operators.
Every valid expression ultimately builds the target using powers of x. Dividing the target by x gives:
target = q * x + rwhere 0 <= r < x.
Any expression for target must resolve this remainder in one of two ways.
The first possibility is to construct q * x and then add r.
The second possibility is to construct (q + 1) * x and subtract (x - r).
These are the only two nearest multiples of x, so every optimal expression must follow one of these two strategies.
The recursive calls compute the optimal cost for the quotient part. The remainder adjustment cost is added according to how many low-power terms are needed.
For targets smaller than x, the algorithm directly compares the only two meaningful constructions:
1 + 1 + ...versus:
x - ...and chooses the smaller operator count.
Memoization guarantees that each subproblem is solved optimally once and reused consistently.
Therefore, the recursion explores all optimal constructions and returns the minimum operator count.
Complexity
The recursion repeatedly divides the target by x.
| Metric | Value | Why |
|---|---|---|
| Time | O(log_x(target)) | Each recursive step reduces the target size |
| Space | O(log_x(target)) | Memoization and recursion depth |
Implementation
from functools import cache
class Solution:
def leastOpsExpressTarget(self, x: int, target: int) -> int:
@cache
def dfs(t: int) -> int:
if t == 0:
return 0
if t < x:
return min(
2 * t - 1,
2 * (x - t),
)
q, r = divmod(t, x)
use_positive = dfs(q) + r
use_negative = dfs(q + 1) + (x - r)
if q > 0:
use_positive += 1
if r > 0:
use_negative += 1
return min(use_positive, use_negative)
return dfs(target)Code Explanation
We memoize recursive results:
@cacheso repeated subproblems are computed only once.
If:
t == 0then no expression is needed:
return 0For small targets:
if t < x:we compare two constructions.
Build directly with ones:
2 * t - 1Example:
2 = x/x + x/xThis uses:
/
+which is 3 operators.
Or subtract from x:
x - (x/x + ...)with cost:
2 * (x - t)For larger targets:
q, r = divmod(t, x)We recursively solve the quotient.
Positive construction:
use_positive = dfs(q) + rNegative construction:
use_negative = dfs(q + 1) + (x - r)Then we add operator costs for connecting pieces:
if q > 0:
use_positive += 1and:
if r > 0:
use_negative += 1Finally:
return min(use_positive, use_negative)chooses the cheaper construction.
Testing
def run_tests():
s = Solution()
assert s.leastOpsExpressTarget(3, 19) == 5
assert s.leastOpsExpressTarget(5, 501) == 8
assert s.leastOpsExpressTarget(100, 100000000) == 3
assert s.leastOpsExpressTarget(3, 1) == 1
assert s.leastOpsExpressTarget(3, 2) == 2
assert s.leastOpsExpressTarget(2, 7) == 4
assert s.leastOpsExpressTarget(2, 1) == 1
assert s.leastOpsExpressTarget(10, 10) == 0
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
(3, 19) | Official-style mixed construction |
(5, 501) | Larger recursive decomposition |
(100, 100000000) | Pure multiplication chain |
(3, 1) | Smallest nonzero target |
(3, 2) | Small target below x |
(2, 7) | Binary-style recursive behavior |
(2, 1) | Single division construction |
(10, 10) | Exact match with no operators needed |