Least Operators to Express Number

hard dynamic programming math base-x

Problem

Given a single positive integer x, you build an expression that uses x repeatedly together with the operators +, −, × and ÷ (standard precedence, no parentheses, no unary minus). Each x written down costs one usage, and the operators between them are counted. Return the least number of operators needed to make the expression equal target.

The trick: x × x × … × x is a power of x and uses (power − 1) operators, while x ÷ x = 1 uses one operator. So every term costs a number of operators tied to which power of x it represents. Process target in base x, and at each digit decide whether to add that many copies of the current power or subtract from the next higher power.

Inputx = 3, target = 19
Output5
19 = 3×3 + 3×3 + 3 ÷ 3 → (×) + (+) + (×) + (+) + (÷) = 5 operators. Two copies of 9 plus one unit (3÷3), each operator counted once.

def least_ops_express_target(x, target):
    # pos = min ops to reach the current low part exactly
    # neg = min ops to overshoot it (so a higher power can subtract back down)
    # Per copy of x**k we pay weight: k==0 -> 2 (x / x), k >= 1 -> k.
    pos = neg = 0
    k = 0
    while target:
        cur = target % x                    # base-x digit at power k
        if k == 0:
            pos = cur * 2                    # cur units, each x / x costs 2
            neg = (x - cur) * 2              # overshoot by (x - cur) units, borrow 1 from x**1
        else:
            pos_cost = cur * k               # place cur copies of x**k
            neg_cost = (x - cur) * k         # place (x - cur) copies to overshoot
            new_pos = min(pos_cost + pos, pos_cost + k + neg)
            new_neg = min(neg_cost + pos, neg_cost - k + neg)
            pos, neg = new_pos, new_neg
        target //= x
        k += 1
    return min(pos, k + neg) - 1             # drop the leading '+' we over-counted
function leastOpsExpressTarget(x, target) {
  let pos = 0, neg = 0, k = 0;
  while (target) {
    const cur = target % x;
    if (k === 0) {
      pos = cur * 2;
      neg = (x - cur) * 2;
    } else {
      const posCost = cur * k;
      const negCost = (x - cur) * k;
      const newPos = Math.min(posCost + pos, posCost + k + neg);
      const newNeg = Math.min(negCost + pos, negCost - k + neg);
      pos = newPos; neg = newNeg;
    }
    target = Math.floor(target / x);
    k++;
  }
  return Math.min(pos, k + neg) - 1;
}
class Solution {
    public int leastOpsExpressTarget(int x, int target) {
        long pos = 0, neg = 0;
        int k = 0;
        while (target > 0) {
            int cur = target % x;
            if (k == 0) {
                pos = (long) cur * 2;
                neg = (long) (x - cur) * 2;
            } else {
                long posCost = (long) cur * k;
                long negCost = (long) (x - cur) * k;
                long newPos = Math.min(posCost + pos, posCost + k + neg);
                long newNeg = Math.min(negCost + pos, negCost - k + neg);
                pos = newPos; neg = newNeg;
            }
            target /= x;
            k++;
        }
        return (int) (Math.min(pos, k + neg) - 1);
    }
}
class Solution {
public:
    int leastOpsExpressTarget(int x, int target) {
        long long pos = 0, neg = 0;
        int k = 0;
        while (target > 0) {
            int cur = target % x;
            if (k == 0) {
                pos = (long long) cur * 2;
                neg = (long long) (x - cur) * 2;
            } else {
                long long posCost = (long long) cur * k;
                long long negCost = (long long) (x - cur) * k;
                long long newPos = min(posCost + pos, posCost + k + neg);
                long long newNeg = min(negCost + pos, negCost - k + neg);
                pos = newPos; neg = newNeg;
            }
            target /= x;
            k++;
        }
        return (int) (min(pos, (long long) k + neg) - 1);
    }
};
Time: O(logx target) Space: O(1)