964. 最少运算符表示数字
题目描述
给定一个正整数 x,我们将编写一个形式为 x (op1) x (op2) x (op3) x … 的表达式,其中每个运算符 op1, op2 等是加法、减法、乘法或除法 (+, -, *, /)。例如,当 x = 3 时,我们可能写 3 * 3 / 3 + 3 - 3,其值为 3。
编写此类表达式时,遵循以下约定:
- 除法运算符 (/) 返回有理数。
- 任何地方都不放置括号。
- 使用通常的运算顺序:乘法和除法先于加法和减法。
- 不允许使用一元负号运算符 (-)。例如,'x - x'是有效表达式,因为它只使用减法,但'-x + x'无效,因为它使用了负号。
我们希望编写一个运算符数量最少的表达式,使得表达式的值等于给定的目标值。返回使用的最少运算符数量。
示例
输入: x = 3, target = 19 输出: 5 解释: 3 * 3 + 3 * 3 + 3 / 3。 表达式包含 5 个操作。
输入: x = 5, target = 501 输出: 8 解释: 5 * 5 * 5 * 5 - 5 * 5 * 5 + 5 / 5。 表达式包含 8 个操作。
输入: x = 100, target = 100000000 输出: 3 解释: 100 * 100 * 100 * 100。 表达式包含 3 个操作。
约束条件
- 2 <= x <= 100
- 1 <= target <= 2 * 10^8
解题思路
- 对于小数值 v <= x,直接使用 1 (x/x) 或 x 减去一些 1 来计算最佳方案。
- 对于较大值,找到最接近的幂 x^k 并尝试:
- 低估:使用 x^(k-1) 并构建余数。
- 高估:使用 x^k 并减去差值(仅当有帮助时)。
- 使用记忆化避免重复计算子问题。
代码实现
typedef struct {
int key;
int val;
} Pair;
static Pair memo[10000];
static int memoSize;
static int dfs(int x, int v) {
// Base case: v <= x
if (v <= x) {
// Option 1: v = 1 + 1 + ... + 1 (v times)
// 1 is x / x -> 1 operator
// plus (v - 1) additions
// total = v (division) + (v - 1) (+) = 2*v - 1
op_add = * v - ;
op_sub = * (x - v);
op_add < op_sub ? op_add : op_sub;
}
( i = ; i < memoSize; ++i) {
(memo[i].key == v) memo[i].val;
}
k = ;
y = ()x * x;
(y < v) {
y *= x;
++k;
}
op1 = (k - ) + dfs(x, v - ()(y / x));
ans = op1;
(y - v < v) {
op2 = k + dfs(x, ()(y - v));
(op2 < ans) ans = op2;
}
memo[memoSize].key = v;
memo[memoSize].val = ans;
++memoSize;
ans;
}
{
memoSize = ;
dfs(x, target);
}


