1921. Eliminate Maximum Number of Monsters - Explanation
Description
Monster i starts dist[i] kilometers away and moves toward the city at speed[i] kilometers per minute. You can eliminate one monster at time 0, then one at each whole minute. If any surviving monster reaches the city, you lose immediately, including when its arrival coincides with a shot time. Return the greatest number of monsters you can eliminate before losing, or the total number if you can eliminate them all.
Example 1:
Input: dist = [2,4,6]
speed = [1,1,1]
Output: 3Explanation: Eliminate the monsters at minutes 0, 1, and 2. Their arrival times are 2, 4, and 6, so all three shots occur in time.
Example 2:
Input: dist = [1,1]
speed = [1,1]
Output: 1Explanation: Both monsters reach the city at minute 1. Only one can be eliminated at minute 0; the other arrives before the next shot.
Example 3:
Input: dist = [3,5]
speed = [2,2]
Output: 2Explanation: The arrival times are 1.5 and 2.5 minutes. Eliminate the first monster at minute 0 and the second at minute 1, before either arrival.
Constraints:
1 <= dist.length <= 100000dist.length == speed.length1 <= dist[i], speed[i] <= 100000
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Sorting - The main solution sorts arrival times to process monsters in order of urgency
- Greedy Algorithms - Understanding that eliminating the earliest-arriving monster first is always optimal
- Min-Heap / Priority Queue - An alternative approach uses a min-heap to extract minimum arrival times on demand
1. Sorting
Intuition
The weapon needs 1 minute to recharge after each shot. To maximize eliminations, we should prioritize monsters that will reach the city soonest. For each monster, we calculate the time it takes to arrive (distance / speed, rounded up). Sorting these arrival times lets us greedily eliminate monsters in order of urgency. If at any minute the earliest uneliminated monster has already arrived, the game ends.
Algorithm
- Compute
minReach[i] = ceil(dist[i] / speed[i])for each monster. - Sort
minReachin ascending order. - Iterate through the sorted array with index
minute:- If
minute >= minReach[minute], the monster arrives before we can shoot it. Return the current count. - Otherwise, increment the elimination count.
- If
- Return the total count if all monsters are eliminated.
class Solution:
def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int:
minReach = [math.ceil(d / s) for d, s in zip(dist, speed)]
minReach.sort()
res = 0
for minute in range(len(minReach)):
if minute >= minReach[minute]:
return res
res += 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
2. Sorting (Overwriting Input Array)
Intuition
This is the same approach as above, but we save space by reusing the input dist array to store arrival times instead of creating a new array. The logic remains identical: compute arrival times, sort, and check if we can eliminate each monster before it arrives.
Algorithm
- Overwrite
dist[i]withceil(dist[i] / speed[i]). - Sort
distin ascending order. - For each
minutefrom0ton - 1:- If
minute >= dist[minute], returnminute.
- If
- Return
nif all monsters are eliminated.
class Solution:
def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int:
for i in range(len(dist)):
dist[i] = math.ceil(dist[i] / speed[i])
dist.sort()
for minute in range(len(dist)):
if minute >= dist[minute]:
return minute
return len(dist)Time & Space Complexity
- Time complexity:
- Space complexity: or depending on the sorting algorithm.
3. Min-Heap
Intuition
Instead of sorting all arrival times upfront, we can use a min-heap to extract the minimum arrival time on demand. This still achieves the same goal of processing monsters in order of how soon they reach the city. We pop from the heap one at a time and check if the monster arrives before the current minute.
Algorithm
- Push
dist[i] / speed[i]into a min-heap for each monster. - Initialize
res = 0to count eliminations. - While the heap is non-empty:
- Pop the smallest arrival time.
- If
res >= arrival_time, the monster has reached the city. Returnres. - Otherwise, increment
res.
- Return
resafter all monsters are eliminated.
class Solution:
def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int:
minHeap = []
for i in range(len(dist)):
heapq.heappush(minHeap, dist[i] / speed[i])
res = 0
while minHeap:
if res >= heapq.heappop(minHeap):
return res
res += 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Using Floor Division Instead of Ceiling
When calculating arrival time, you need ceil(dist[i] / speed[i]) because a monster at distance 3 with speed 2 arrives at minute 2 (not 1.5). Using floor division or integer division without rounding up causes incorrect arrival time calculations, leading to wrong elimination counts.
Forgetting to Sort the Arrival Times
The greedy approach only works when monsters are processed in order of their arrival times. Without sorting, you might try to eliminate a monster that arrives later while an earlier one reaches the city, giving incorrect results.
Off-by-One Error in the Comparison
The condition should be minute >= minReach[minute] (monster arrives at or before the current minute), not minute > minReach[minute]. If a monster arrives exactly at minute 2 and we're at minute 2, we cannot eliminate it in time since we shoot at the start of each minute.
Sign in to join the discussion