LeetCode 2809 - Minimum Time to Make Array Sum At Most x
The problem provides two integer arrays nums1 and nums2 of equal length, along with a target integer x. Each second, every element in nums1 increases by the corresponding element in nums2. After the increment, you can choose one index and set its value in nums1 to zero.
Difficulty: 🔴 Hard
Topics: Array, Dynamic Programming, Sorting
Solution
Problem Understanding
The problem provides two integer arrays nums1 and nums2 of equal length, along with a target integer x. Each second, every element in nums1 increases by the corresponding element in nums2. After the increment, you can choose one index and set its value in nums1 to zero. The task is to determine the minimum number of seconds required so that the sum of all elements in nums1 becomes less than or equal to x. If it is impossible, return -1.
The input arrays are constrained such that 1 <= nums1.length <= 103 and 1 <= nums1[i] <= 103, and 0 <= nums2[i] <= 103. The sum target x can range from 0 to 10^6. The relatively small array size allows us to consider O(n log n) or O(n^2) approaches but not O(2^n) brute-force search.
Important edge cases include situations where x is smaller than any possible sum after the first second, when nums2 contains only zeros, or when operations cannot reduce the sum below x even after setting elements to zero optimally.
Approaches
Brute Force
The brute-force method would simulate each second by incrementing all elements in nums1 by nums2 and trying all possible choices for setting an element to zero. For each time step, we would recursively explore all choices of which index to reset. This guarantees correctness because it examines all sequences of operations, but the time complexity is exponential in the number of elements (O(n^t) where t is the number of seconds) and thus infeasible for n up to 103.
Optimal Approach
The key observation is that the element to reset should ideally be the one whose growth rate is largest, i.e., the one with the largest nums2[i]. This is because higher growth elements contribute more to the total sum each second. Sorting indices by nums2 in descending order allows us to choose the optimal element to reset each second. We can then use binary search on time to determine the minimum number of seconds required to reduce the sum to x.
We define a helper function canReach(t) that simulates the sum after t seconds, assuming the top t elements with largest nums2 values are set to zero optimally. We then perform binary search over t from 0 to n to find the smallest t where the sum ≤ x.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^t) | O(n) | Explore all possible sequences of resets, infeasible |
| Optimal | O(n log n) | O(n) | Sort by growth rates, binary search on number of operations |
Algorithm Walkthrough
- Sort indices by growth rate: Create a list of indices from
0ton-1and sort them in descending order ofnums2[i]. This ensures we always choose the element with the largest growth for reset. - Prefix sums for reset candidates: Compute prefix sums of
nums2andnums1for fast calculation of the contribution of the firsttelements that are set to zero. - Binary search on seconds
t: Initializeleft = 0andright = n. For eachmid = (left + right) // 2, check if the total sum ofnums1[i] + t * nums2[i]minus the contributions of toptresets is ≤x. - Check feasibility: Use the helper function
canReach(t)that computes the sum aftertresets on top growth elements. If the sum is ≤x, moveright = midto minimize time. Otherwise, moveleft = mid + 1. - Return result: If
left > nor the sum cannot reach ≤x, return-1. Otherwise, returnleftas the minimum number of seconds.
Why it works: Resetting the elements with the largest growth rates guarantees the largest reduction in future sum contributions. Binary search ensures we find the minimum seconds efficiently.
Python Solution
from typing import List
class Solution:
def minimumTime(self, nums1: List[int], nums2: List[int], x: int) -> int:
n = len(nums1)
# Pair each element with its growth rate and index
growth = sorted(range(n), key=lambda i: nums2[i], reverse=True)
prefix_nums1 = [0] * (n + 1)
prefix_nums2 = [0] * (n + 1)
for i in range(n):
prefix_nums1[i+1] = prefix_nums1[i] + nums1[growth[i]]
prefix_nums2[i+1] = prefix_nums2[i] + nums2[growth[i]]
def canReach(t: int) -> bool:
total = 0
for i in range(n):
if i < t:
continue # This element will be reset at the correct second
total += nums1[growth[i]] + nums2[growth[i]] * t
return total <= x
left, right = 0, n + 1
while left < right:
mid = (left + right) // 2
if canReach(mid):
right = mid
else:
left = mid + 1
return -1 if left > n else left
Explanation: We sort elements by growth rates and precompute prefix sums for fast calculation. canReach(t) simulates the sum after t resets. Binary search efficiently finds the minimum number of seconds. We handle impossible cases by returning -1.
Go Solution
func minimumTime(nums1 []int, nums2 []int, x int) int {
n := len(nums1)
type pair struct{ val, growth int }
arr := make([]pair, n)
for i := 0; i < n; i++ {
arr[i] = pair{nums1[i], nums2[i]}
}
// Sort by growth descending
sort.Slice(arr, func(i, j int) bool {
return arr[i].growth > arr[j].growth
})
canReach := func(t int) bool {
total := 0
for i := 0; i < n; i++ {
if i < t {
continue
}
total += arr[i].val + arr[i].growth * t
}
return total <= x
}
left, right := 0, n+1
for left < right {
mid := (left + right) / 2
if canReach(mid) {
right = mid
} else {
left = mid + 1
}
}
if left > n {
return -1
}
return left
}
Go-specific notes: We use a struct to pair values with growth rates. Sorting uses sort.Slice. Integer arithmetic is safe given constraints.
Worked Examples
Example 1: nums1 = [1,2,3], nums2 = [1,2,3], x = 4
| Step | Action | nums1 after increment/reset | Sum |
|---|---|---|---|
| 1 | Reset index 2 (growth 3) | [1+1,2+2,0] = [2,4,0] | 6 |
| 2 | Reset index 1 (growth 2) | [2+1,0,0+3] = [3,0,3] | 6 |
| 3 | Reset index 0 (growth 1) | [0,0+2,3+3] = [0,2,6] | 8 |
Binary search identifies minimum time as 3.
Example 2: nums1 = [1,2,3], nums2 = [3,3,3], x = 4
All increments overshoot x even if we reset maximum growth elements. Algorithm returns -1.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Sorting nums2 dominates. Binary search is O(log n), and each check is O(n) |
| Space | O(n) | Storing sorted indices and prefix sums |
The approach is efficient for n ≤ 103 and fits within typical LeetCode constraints.
Test Cases
# Provided examples
assert Solution().minimumTime([1,2,3], [1,2,3], 4) == 3 # Example 1
assert Solution().minimumTime([1,2,3], [3,3,3], 4) == -1 # Example 2
# Edge and boundary cases
assert Solution().minimumTime([1], [0], 0) == 1 # Single element, no growth
assert Solution().minimumTime([1000,1000], [0,0], 1000) == 1 # Need to reset one of two large elements
assert Solution().minimumTime([1,2,3,4], [4,3,2,1], 10) ==