class Solution:
def leastOpsExpressTarget(self, x: int, target: int) -> int:
from functools import lru_cache
@lru_cache(None)
def dp(i, target):
if target == 0:
return 0
if target == 1:
return i # x / x if i > 0, else x^0
if i >= 39:
return float('inf')
t, r = divmod(target, x)
# Option 1: use r times x^i, plus the rest
cost1 = r * i + dp(i + 1, t)
# Option 2: use (x - r) times negative x^i, plus the rest (+1 for operator)
cost2 = (x - r) * i + dp(i + 1, t + 1)
if i == 0:
return min(cost1, cost2 + 1)
return min(cost1, cost2 + 1)
return dp(0, target) - 1
class Solution {
public:
unordered_map memo;
int x;
int dp(int i, long long target) {
if (target == 0) return 0;
if (target == 1) return i;
if (i >= 39) return INT_MAX / 2;
long long key = target * 100 + i;
if (memo.count(key)) return memo[key];
long long t = target / x, r = target % x;
int cost1 = r * i + dp(i + 1, t);
int cost2 = (x - r) * i + dp(i + 1, t + 1);
int res = min(cost1, cost2 + 1);
memo[key] = res;
return res;
}
int leastOpsExpressTarget(int x, int target) {
this->x = x;
return dp(0, target) - 1;
}
};
class Solution {
Map<String, Integer> memo = new HashMap<>();
public int leastOpsExpressTarget(int x, int target) {
return dp(x, 0, target) - 1;
}
private int dp(int x, int i, int target) {
if (target == 0) return 0;
if (target == 1) return i;
if (i >= 39) return Integer.MAX_VALUE / 2;
String key = target + "," + i;
if (memo.containsKey(key)) return memo.get(key);
int t = target / x, r = target % x;
int cost1 = r * i + dp(x, i + 1, t);
int cost2 = (x - r) * i + dp(x, i + 1, t + 1);
int res = Math.min(cost1, cost2 + 1);
memo.put(key, res);
return res;
}
}
var leastOpsExpressTarget = function(x, target) {
const memo = new Map();
function dp(i, target) {
if (target === 0) return 0;
if (target === 1) return i;
if (i >= 39) return Number.MAX_SAFE_INTEGER;
let key = target + ',' + i;
if (memo.has(key)) return memo.get(key);
let t = Math.floor(target / x), r = target % x;
let cost1 = r * i + dp(i + 1, t);
let cost2 = (x - r) * i + dp(i + 1, t + 1);
let res = Math.min(cost1, cost2 + 1);
memo.set(key, res);
return res;
}
return dp(0, target) - 1;
};
Given two positive integers x
and target
, you are to express target
using only the number x
and the operators +
, -
, *
, and /
. Each operator can be used any number of times, but you cannot use parentheses and must respect the usual operator precedence (multiplication and division before addition and subtraction). You may use x
as many times as needed, and can use powers of x
(like x * x
, x * x * x
, etc.).
Your goal is to find the least number of operators needed to express target
in terms of x
.
Constraints:
x
itself does not count as an operator.
At first glance, it might seem we could just build up target
using repeated multiplications and additions of x
. However, as target
can be very large, a brute-force approach of trying all combinations is not feasible.
The key realization is that every number can be represented in base x
. If we write target
in base x
, each digit tells us how many times we need to use a certain power of x
(i.e., x^i
).
For each digit, we have two choices:
x^i
(costs digit * number of operators to write x^i
), and solve for the rest.x^i
in the negative direction, and add one to the next higher power (i.e., handle carry/borrow).dp(i, target)
that returns the minimal number of operators needed to express target
using powers of x
starting from x^i
.target == 0
, no operators needed.target == 1
, it takes i
operators to write x / x / ... / x
(i times).target
by x
to get quotient t
and remainder r
.r
times x^i
, costing r * i
operators, then solve for dp(i+1, t)
.(x - r)
times x^i
in the negative direction, costing (x - r) * i
operators, then solve for dp(i+1, t+1)
(carry over).(i, target)
in a memoization table or cache.i == 0
, using x^0
(which is 1) requires a special case: we must use x / x
, which uses one operator.x
in the highest power does not require an operator.i = 0
and the given target
.
Let's use x = 3
and target = 19
as an example.
x^0
), digit is 1. We can write this as x / x
(costs 1 operator).
x^1
, digit is 0. No need to use x^1
.
x^2
, digit is 2. We can write this as x * x + x * x
(costs 2 * 2 = 4 operators: two multiplications and one addition).
x^0
) + 0 (for x^1
) + 4 (for x^2
) = 5 operators, but the recursion may find a more optimal way by considering negative representations (using subtraction and carrying over).
The DP solution automatically considers all options and finds the minimum, which for this example is 5.
The problem leverages the relationship between number representation in base x
and the minimal use of arithmetic operators. By using dynamic programming and memoization, we efficiently explore all possible ways to build the target number using the least operators. The key insight is to recursively break down the problem by powers of x
, always considering both using the remainder and borrowing from the next higher power, ensuring the optimal solution is found. This approach transforms a seemingly brute-force problem into a tractable one by smart state management and recursion.