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
: takesworkerTimes[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
.
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:
-
Initialize a counter
h = 0
to track the total height reduction -
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
-
Return
True
if the total height reductionh >= 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 EvaluatorExample 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 requiresO(log M)
iterations whereM = 10^16
- For each iteration of the binary search, the
check
function is called - Inside the
check
function, we iterate through alln
workers inworkerTimes
, performing a constant-time calculation (square root and arithmetic operations) for each worker - Therefore, each call to
check
takesO(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 variableh
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
Which of the two traversal algorithms (BFS and DFS) can be used to find whether two nodes are connected?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Want a Structured Path to Master System Design Too? Don’t Miss This!