Minimum Number of Seconds to Make Mountain Height Zero

medium binary search greedy math

Problem

A mountain has height mountainHeight. Worker i has base time workerTimes[i]: its 1st unit costs workerTimes[i] seconds, its 2nd unit workerTimes[i]·2, …, its x-th unit workerTimes[i]·x. So reducing x units costs workerTimes[i]·x·(x+1)/2. All workers act at once, so total time is the maximum worker time. Return the minimum seconds to bring the height to 0.

InputmountainHeight = 4, workerTimes = [2,1,1]
Output3
In 3 seconds: worker 0 does 1 unit (2s), worker 1 does 2 units (1+2=3s), worker 2 does 1 unit (1s). Total reduced = 4 = mountainHeight, and max time = 3.

def minNumberOfSeconds(mountainHeight, workerTimes):
    # units a worker (base t) can do within budget T: t*x*(x+1)/2 <= T
    def units(t, T):
        cap = T // t                      # need x*(x+1)/2 <= cap
        x = (isqrt(1 + 8 * cap) - 1) // 2  # closed-form, then nudge
        while (x + 1) * (x + 2) // 2 <= cap:
            x += 1
        return x

    def feasible(T):                      # can all workers clear H in T?
        total = 0
        for t in workerTimes:
            total += units(t, T)
            if total >= mountainHeight:
                return True
        return False

    lo, hi = 1, min(workerTimes) * mountainHeight * (mountainHeight + 1) // 2
    while lo < hi:                        # smallest feasible T
        mid = (lo + hi) // 2
        if feasible(mid):
            hi = mid
        else:
            lo = mid + 1
    return lo
function minNumberOfSeconds(mountainHeight, workerTimes) {
  // units a worker (base t) can do within budget T: t*x*(x+1)/2 <= T
  const units = (t, T) => {
    const cap = Math.floor(T / t);          // need x*(x+1)/2 <= cap
    let x = Math.floor((Math.sqrt(1 + 8 * cap) - 1) / 2);
    while ((x + 1) * (x + 2) / 2 <= cap) x++;
    return x;
  };
  const feasible = (T) => {                  // can all workers clear H in T?
    let total = 0;
    for (const t of workerTimes) {
      total += units(t, T);
      if (total >= mountainHeight) return true;
    }
    return false;
  };
  let lo = 1;
  let hi = Math.min(...workerTimes) * mountainHeight * (mountainHeight + 1) / 2;
  while (lo < hi) {                          // smallest feasible T
    const mid = Math.floor((lo + hi) / 2);
    if (feasible(mid)) hi = mid;
    else lo = mid + 1;
  }
  return lo;
}
long minNumberOfSeconds(int mountainHeight, int[] workerTimes) {
    long lo = 1, hi = 0, minT = Integer.MAX_VALUE;
    for (int t : workerTimes) minT = Math.min(minT, t);
    hi = minT * (long) mountainHeight * (mountainHeight + 1) / 2;
    while (lo < hi) {                        // smallest feasible T
        long mid = (lo + hi) / 2;
        if (feasible(mid, mountainHeight, workerTimes)) hi = mid;
        else lo = mid + 1;
    }
    return lo;
}

boolean feasible(long T, int H, int[] workerTimes) {
    long total = 0;
    for (int t : workerTimes) {
        long cap = T / t;                   // need x*(x+1)/2 <= cap
        long x = (long) ((Math.sqrt(1 + 8.0 * cap) - 1) / 2);
        while ((x + 1) * (x + 2) / 2 <= cap) x++;
        total += x;
        if (total >= H) return true;
    }
    return false;
}
long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
    auto units = [](long long t, long long T) {
        long long cap = T / t;              // need x*(x+1)/2 <= cap
        long long x = (long long)((sqrt(1 + 8.0 * cap) - 1) / 2);
        while ((x + 1) * (x + 2) / 2 <= cap) x++;
        return x;
    };
    auto feasible = [&](long long T) {       // can all workers clear H?
        long long total = 0;
        for (int t : workerTimes) {
            total += units(t, T);
            if (total >= mountainHeight) return true;
        }
        return false;
    };
    long long mn = *min_element(workerTimes.begin(), workerTimes.end());
    long long lo = 1, hi = mn * (long long) mountainHeight * (mountainHeight + 1) / 2;
    while (lo < hi) {                        // smallest feasible T
        long long mid = (lo + hi) / 2;
        if (feasible(mid)) hi = mid;
        else lo = mid + 1;
    }
    return lo;
}
Time: O(n · log(minT · H²)) Space: O(1)