Facebook Pixel

3296. Minimum Number of Seconds to Make Mountain Height Zero

Problem Description

You have a mountain with a given height mountainHeight and multiple workers who can work simultaneously to reduce this height to 0. Each worker has their own working speed defined in the array workerTimes.

For each worker i, the time required to reduce the mountain height follows a specific pattern:

  • To reduce height by 1: takes workerTimes[i] seconds
  • To reduce height by 2: takes workerTimes[i] + workerTimes[i] * 2 seconds
  • To reduce height by 3: takes workerTimes[i] + workerTimes[i] * 2 + workerTimes[i] * 3 seconds
  • In general, to reduce height by x: takes workerTimes[i] * (1 + 2 + ... + x) seconds

The key insight is that this forms an arithmetic series. To reduce the height by x, worker i needs: workerTimes[i] * (1 + 2 + ... + x) = workerTimes[i] * x * (x + 1) / 2 seconds

Since workers operate simultaneously, the total time taken is determined by when all workers collectively reduce the mountain height to 0. Each worker can reduce a different amount of height, and we need to find the optimal distribution that minimizes the total time.

The solution uses binary search on the time t. For a given time t, we calculate how much height each worker can reduce. Using the formula above, if worker i reduces height by h' in time t, then: workerTimes[i] * h' * (h' + 1) / 2 ≤ t

Solving this quadratic inequality gives us the maximum height each worker can reduce in time t. The binary search finds the minimum time where the sum of all heights reduced by workers is at least mountainHeight.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key observation is that if we can reduce the mountain to height 0 in t seconds, then we can definitely do it in any time greater than t. This monotonic property immediately suggests binary search on the answer.

Why binary search on time? Consider the alternative - trying to directly calculate the optimal height distribution among workers. This becomes a complex optimization problem because each worker has different efficiency and the cost function is quadratic. However, if we fix the time t, we can easily calculate the maximum height each worker can reduce independently.

For a fixed time t, each worker works independently and we want to maximize their contribution. Given worker i has t seconds, we need to find the maximum height h' they can reduce where: workerTimes[i] * (1 + 2 + ... + h') ≤ t

Using the arithmetic series sum formula: workerTimes[i] * h' * (h' + 1) / 2 ≤ t

This is a quadratic inequality in h'. Rearranging: h'² + h' - 2t/workerTimes[i] ≤ 0

Using the quadratic formula and taking the positive root: h' ≤ (-1 + √(1 + 8t/workerTimes[i])) / 2

Which simplifies to: h' ≤ ⌊√(2t/workerTimes[i] + 1/4) - 1/2⌋

Once we can calculate each worker's maximum contribution for a given time t, we simply sum these contributions. If the total is at least mountainHeight, then time t is sufficient. Otherwise, we need more time.

The binary search bounds are straightforward: the minimum time is close to 0, and the maximum can be estimated by considering the worst case where one slow worker does all the work. Since workerTimes[i] ≤ 10^6 and mountainHeight ≤ 10^5, the upper bound of 10^16 is safe.

Learn more about Greedy, Math, Binary Search and Heap (Priority Queue) patterns.

Solution Approach

The solution implements a binary search to find the minimum time required. Here's how the implementation works:

Binary Search Setup: We use bisect_left to find the leftmost position where the condition becomes True. The search range is from 0 to 10^16, which safely covers all possible time values.

Check Function: The check(t) function determines if time t is sufficient for workers to reduce the mountain to height 0:

  1. Initialize a counter h = 0 to track the total height reduction

  2. For each worker with time workerTimes[i], calculate the maximum height they can reduce using the formula:

    h += int(sqrt(2 * t / wt + 1 / 4) - 1 / 2)

    This formula comes from solving the quadratic inequality: wt * h' * (h' + 1) / 2 ≤ t

    Which gives us: h' ≤ √(2t/wt + 1/4) - 1/2

  3. Return True if the total height reduction h >= mountainHeight

Why This Formula Works: Given a worker needs wt * h' * (h' + 1) / 2 seconds to reduce height by h', we rearrange to find the maximum h' possible in time t:

  • Start with: wt * h' * (h' + 1) / 2 ≤ t
  • Multiply by 2: wt * h' * (h' + 1) ≤ 2t
  • Expand: wt * h'² + wt * h' ≤ 2t
  • Rearrange: h'² + h' - 2t/wt ≤ 0
  • Complete the square: (h' + 1/2)² ≤ 2t/wt + 1/4
  • Take square root: h' + 1/2 ≤ √(2t/wt + 1/4)
  • Solve for h': h' ≤ √(2t/wt + 1/4) - 1/2

Binary Search Execution: The bisect_left function efficiently finds the minimum time where check(t) returns True. It performs binary search on the range [0, 10^16], using the key=check parameter to evaluate each midpoint.

The time complexity is O(n * log(10^16)) where n is the number of workers, as we perform the check function O(log(10^16)) times, and each check takes O(n) time to iterate through all workers.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example with mountainHeight = 10 and workerTimes = [2, 4].

Step 1: Understanding Worker Capabilities

Worker 0 (time = 2):

  • Reduce by 1: takes 2×1 = 2 seconds
  • Reduce by 2: takes 2×(1+2) = 6 seconds
  • Reduce by 3: takes 2×(1+2+3) = 12 seconds
  • Reduce by 4: takes 2×(1+2+3+4) = 20 seconds

Worker 1 (time = 4):

  • Reduce by 1: takes 4×1 = 4 seconds
  • Reduce by 2: takes 4×(1+2) = 12 seconds
  • Reduce by 3: takes 4×(1+2+3) = 24 seconds

Step 2: Binary Search Process

We binary search on time from 0 to 10^16. Let's check some key time values:

Check t = 10:

  • Worker 0: Using formula h' ≤ √(2×10/2 + 1/4) - 1/2 = √(10.25) - 0.5 ≈ 2.7, so can reduce 2 units
  • Worker 1: Using formula h' ≤ √(2×10/4 + 1/4) - 1/2 = √(5.25) - 0.5 ≈ 1.8, so can reduce 1 unit
  • Total reduction: 2 + 1 = 3 < 10 ❌ (not enough)

Check t = 20:

  • Worker 0: h' ≤ √(2×20/2 + 1/4) - 1/2 = √(20.25) - 0.5 ≈ 4.0, so can reduce 4 units
  • Worker 1: h' ≤ √(2×20/4 + 1/4) - 1/2 = √(10.25) - 0.5 ≈ 2.7, so can reduce 2 units
  • Total reduction: 4 + 2 = 6 < 10 ❌ (not enough)

Check t = 40:

  • Worker 0: h' ≤ √(2×40/2 + 1/4) - 1/2 = √(40.25) - 0.5 ≈ 5.8, so can reduce 5 units
  • Worker 1: h' ≤ √(2×40/4 + 1/4) - 1/2 = √(20.25) - 0.5 ≈ 4.0, so can reduce 4 units
  • Total reduction: 5 + 4 = 9 < 10 ❌ (not enough)

Check t = 42:

  • Worker 0: h' ≤ √(2×42/2 + 1/4) - 1/2 = √(42.25) - 0.5 ≈ 6.0, so can reduce 6 units
  • Worker 1: h' ≤ √(2×42/4 + 1/4) - 1/2 = √(21.25) - 0.5 ≈ 4.1, so can reduce 4 units
  • Total reduction: 6 + 4 = 10 ✓ (sufficient!)

Step 3: Verification

Let's verify t = 42 is correct:

  • Worker 0 reduces 6 units: takes 2×(1+2+3+4+5+6) = 2×21 = 42 seconds
  • Worker 1 reduces 4 units: takes 4×(1+2+3+4) = 4×10 = 40 seconds
  • Both finish within 42 seconds, and 6 + 4 = 10 (mountain fully reduced)

Binary search continues narrowing down between values where the check fails and succeeds, ultimately finding that 42 is the minimum time needed.

Solution Implementation

1from typing import List
2from math import sqrt
3from bisect import bisect_left
4
5class Solution:
6    def minNumberOfSeconds(self, mountainHeight: int, workerTimes: List[int]) -> int:
7        """
8        Find the minimum number of seconds needed to reduce the mountain to the target height.
9      
10        Each worker reduces height following the pattern: 1 + 2 + 3 + ... + n units,
11        where the time taken is workerTime * (1 + 2 + ... + n) = workerTime * n * (n + 1) / 2
12      
13        Args:
14            mountainHeight: The target height to reduce the mountain to
15            workerTimes: List of time coefficients for each worker
16          
17        Returns:
18            Minimum seconds needed to complete the task
19        """
20      
21        def can_complete_in_time(time_limit: int) -> bool:
22            """
23            Check if workers can reduce the mountain to target height within given time.
24          
25            For each worker with time coefficient wt:
26            - If they work for n rounds, time spent = wt * n * (n + 1) / 2
27            - We need to find max n such that wt * n * (n + 1) / 2 <= time_limit
28            - Using quadratic formula: n = (-1 + sqrt(1 + 8 * time_limit / wt)) / 2
29          
30            Args:
31                time_limit: Maximum time allowed
32              
33            Returns:
34                True if mountain can be reduced to target height within time limit
35            """
36            total_height_reduced = 0
37          
38            for worker_time in workerTimes:
39                # Calculate maximum rounds this worker can complete within time_limit
40                # Using the quadratic formula solution for n
41                max_rounds = int(sqrt(2 * time_limit / worker_time + 0.25) - 0.5)
42                total_height_reduced += max_rounds
43          
44            return total_height_reduced >= mountainHeight
45      
46        # Binary search for minimum time needed
47        # Search space: [0, 10^16) which should cover all practical cases
48        max_search_bound = 10**16
49        minimum_time = bisect_left(range(max_search_bound), True, key=can_complete_in_time)
50      
51        return minimum_time
52
1class Solution {
2    private int mountainHeight;
3    private int[] workerTimes;
4
5    /**
6     * Finds the minimum number of seconds needed for workers to reduce the mountain to ground level.
7     * Uses binary search to find the optimal time.
8     * 
9     * @param mountainHeight The initial height of the mountain
10     * @param workerTimes Array where workerTimes[i] represents the time for worker i to reduce height by 1
11     * @return The minimum number of seconds needed
12     */
13    public long minNumberOfSeconds(int mountainHeight, int[] workerTimes) {
14        this.mountainHeight = mountainHeight;
15        this.workerTimes = workerTimes;
16      
17        // Binary search boundaries: minimum 1 second, maximum 10^16 seconds
18        long left = 1;
19        long right = (long) 1e16;
20      
21        // Binary search for the minimum time
22        while (left < right) {
23            long mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
24          
25            if (canCompleteInTime(mid)) {
26                // If we can complete in 'mid' seconds, try to find a smaller time
27                right = mid;
28            } else {
29                // If we cannot complete in 'mid' seconds, we need more time
30                left = mid + 1;
31            }
32        }
33      
34        return left;
35    }
36
37    /**
38     * Checks if workers can reduce the mountain to ground level within given time.
39     * Each worker reduces height following arithmetic progression: 1st unit takes workerTime[i],
40     * 2nd unit takes 2*workerTime[i], nth unit takes n*workerTime[i].
41     * Total time for n units: workerTime[i] * (1 + 2 + ... + n) = workerTime[i] * n * (n + 1) / 2
42     * 
43     * @param timeLimit The time limit to check
44     * @return true if the mountain can be reduced within timeLimit, false otherwise
45     */
46    private boolean canCompleteInTime(long timeLimit) {
47        long totalHeightReduced = 0;
48      
49        for (int workerTime : workerTimes) {
50            // For each worker, calculate maximum height they can reduce in timeLimit
51            // Using formula: workerTime * n * (n + 1) / 2 <= timeLimit
52            // Solving for n: n = (-1 + sqrt(1 + 8 * timeLimit / workerTime)) / 2
53            // Simplified: n = sqrt(2 * timeLimit / workerTime + 0.25) - 0.5
54            long maxHeightByWorker = (long) (Math.sqrt(timeLimit * 2.0 / workerTime + 0.25) - 0.5);
55            totalHeightReduced += maxHeightByWorker;
56        }
57      
58        // Check if total height reduced by all workers meets the requirement
59        return totalHeightReduced >= mountainHeight;
60    }
61}
62
1class Solution {
2public:
3    long long minNumberOfSeconds(int mountainHeight, vector<int>& workerTimes) {
4        using ll = long long;
5      
6        // Binary search bounds: minimum 1 second, maximum 10^16 seconds
7        ll left = 1;
8        ll right = 1e16;
9      
10        // Lambda function to check if given time is sufficient to reduce mountain to 0
11        auto canCompleteInTime = [&](ll timeLimit) -> bool {
12            ll totalHeightReduced = 0;
13          
14            // For each worker, calculate how much height they can reduce in given time
15            for (int& workerTime : workerTimes) {
16                // Worker reduces height by 1, 2, 3, ... units
17                // Time taken: workerTime * (1 + 2 + 3 + ... + n) = workerTime * n * (n + 1) / 2
18                // Given timeLimit, solve for n: workerTime * n * (n + 1) / 2 <= timeLimit
19                // This gives us a quadratic equation: n^2 + n - 2*timeLimit/workerTime <= 0
20                // Using quadratic formula: n = (-1 + sqrt(1 + 8*timeLimit/workerTime)) / 2
21                // Simplified calculation below
22                totalHeightReduced += static_cast<ll>(sqrt(timeLimit * 2.0 / workerTime + 0.25) - 0.5);
23            }
24          
25            // Check if total height reduced meets or exceeds the mountain height
26            return totalHeightReduced >= mountainHeight;
27        };
28      
29        // Binary search to find minimum time needed
30        while (left < right) {
31            ll mid = (left + right) >> 1;  // Equivalent to (left + right) / 2
32          
33            if (canCompleteInTime(mid)) {
34                // If we can complete in 'mid' seconds, try to find a smaller time
35                right = mid;
36            } else {
37                // If we cannot complete in 'mid' seconds, we need more time
38                left = mid + 1;
39            }
40        }
41      
42        // Return the minimum number of seconds needed
43        return left;
44    }
45};
46
1/**
2 * Calculates the minimum number of seconds needed for workers to reduce a mountain to height 0.
3 * Each worker reduces the mountain height by 1, 2, 3, ... units in successive operations,
4 * taking workerTime * (1 + 2 + ... + k) seconds for k operations.
5 * 
6 * @param mountainHeight - The initial height of the mountain to be reduced
7 * @param workerTimes - Array where workerTimes[i] is the time multiplier for worker i
8 * @returns The minimum number of seconds needed to reduce the mountain to height 0
9 */
10function minNumberOfSeconds(mountainHeight: number, workerTimes: number[]): number {
11    /**
12     * Checks if the given time is sufficient for workers to reduce the mountain.
13     * Uses the formula: time = workerTime * k * (k + 1) / 2 for k operations
14     * Solving for k: k = floor((-1 + sqrt(1 + 8 * time / workerTime)) / 2)
15     * 
16     * @param timeLimit - The time limit to check
17     * @returns true if workers can reduce the mountain within the time limit
18     */
19    const canCompleteInTime = (timeLimit: bigint): boolean => {
20        let totalHeightReduced = BigInt(0);
21      
22        for (const workerTime of workerTimes) {
23            // Calculate maximum operations this worker can perform within time limit
24            // Using quadratic formula: k = (-0.5 + sqrt(0.25 + 2 * time / workerTime))
25            const maxOperations = Math.floor(
26                Math.sqrt((Number(timeLimit) * 2.0) / workerTime + 0.25) - 0.5
27            );
28            totalHeightReduced += BigInt(maxOperations);
29        }
30      
31        return totalHeightReduced >= BigInt(mountainHeight);
32    };
33
34    // Binary search for minimum time needed
35    let leftBound = BigInt(1);
36    let rightBound = BigInt(1e16); // Upper bound for time
37
38    while (leftBound < rightBound) {
39        // Calculate midpoint using bit shift for division by 2
40        const midTime = (leftBound + rightBound) >> BigInt(1);
41      
42        if (canCompleteInTime(midTime)) {
43            // If possible within midTime, try to find a smaller time
44            rightBound = midTime;
45        } else {
46            // If not possible, need more time
47            leftBound = midTime + BigInt(1);
48        }
49    }
50
51    return Number(leftBound);
52}
53

Time and Space Complexity

Time Complexity: O(n × log M), where n is the number of workers (length of workerTimes), and M is the upper bound of the binary search range, which is 10^16 in this problem.

The analysis breaks down as follows:

  • The bisect_left function performs a binary search over the range [0, 10^16), which requires O(log M) iterations where M = 10^16
  • For each iteration of the binary search, the check function is called
  • Inside the check function, we iterate through all n workers in workerTimes, performing a constant-time calculation (square root and arithmetic operations) for each worker
  • Therefore, each call to check takes O(n) time
  • The total time complexity is O(n) × O(log M) = O(n × log M)

Space Complexity: O(1)

The space analysis:

  • The check function only uses a constant amount of extra space for the variable h and loop variable
  • The binary search itself only requires constant space for maintaining search boundaries
  • No additional data structures are created that scale with the input size
  • Therefore, the overall space complexity is O(1) (constant space)

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Integer Overflow in Binary Search Upper Bound

Pitfall: Using an inadequate upper bound for binary search can lead to incorrect results for large inputs. The formula workerTime * n * (n + 1) / 2 grows quadratically, and with large mountainHeight and workerTimes, the required time can be substantial.

Solution: Use a sufficiently large upper bound. A safe approach is to calculate the worst-case scenario:

# Calculate a proper upper bound
max_worker_time = max(workerTimes)
# Worst case: single slowest worker does all the work
upper_bound = max_worker_time * mountainHeight * (mountainHeight + 1) // 2

2. Floating Point Precision Errors

Pitfall: The quadratic formula involves square roots and division, which can introduce floating-point precision errors:

# Potential precision issues
max_rounds = int(sqrt(2 * time_limit / worker_time + 0.25) - 0.5)

When time_limit is very large or worker_time is very small, the calculation might lose precision.

Solution: Add a small epsilon for comparison or use integer arithmetic where possible:

def can_complete_in_time(time_limit: int) -> bool:
    total_height_reduced = 0
  
    for worker_time in workerTimes:
        # Binary search for max_rounds instead of using floating-point formula
        left, right = 0, mountainHeight
        while left < right:
            mid = (left + right + 1) // 2
            if worker_time * mid * (mid + 1) // 2 <= time_limit:
                left = mid
            else:
                right = mid - 1
        total_height_reduced += left
  
    return total_height_reduced >= mountainHeight

3. Incorrect Quadratic Formula Application

Pitfall: Misapplying the quadratic formula or using the wrong root. The inequality wt * h * (h + 1) / 2 ≤ t leads to a quadratic equation, and choosing the wrong root or making algebraic errors is common.

Solution: Verify the formula derivation and always take the positive root:

# Correct formula with explicit steps
discriminant = 1 + 8 * time_limit / worker_time
if discriminant < 0:
    max_rounds = 0
else:
    # We need the positive root: (-1 + sqrt(discriminant)) / 2
    max_rounds = int((-1 + sqrt(discriminant)) / 2)

4. Division by Zero

Pitfall: If any workerTimes[i] is 0, division by zero occurs in the formula.

Solution: Add validation for edge cases:

def can_complete_in_time(time_limit: int) -> bool:
    total_height_reduced = 0
  
    for worker_time in workerTimes:
        if worker_time == 0:
            # Worker with 0 time can reduce infinite height instantly
            return True
        max_rounds = int(sqrt(2 * time_limit / worker_time + 0.25) - 0.5)
        total_height_reduced += max_rounds
  
    return total_height_reduced >= mountainHeight

5. Early Termination Optimization Missing

Pitfall: Continuing to calculate even after we've already reduced enough height wastes computation.

Solution: Add early termination:

def can_complete_in_time(time_limit: int) -> bool:
    total_height_reduced = 0
  
    for worker_time in workerTimes:
        max_rounds = int(sqrt(2 * time_limit / worker_time + 0.25) - 0.5)
        total_height_reduced += max_rounds
      
        # Early termination if we've already reduced enough
        if total_height_reduced >= mountainHeight:
            return True
  
    return total_height_reduced >= mountainHeight
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More