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 minReach in 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.
Return the total count if all monsters are eliminated.
classSolution:defeliminateMaximum(self, dist: List[int], speed: List[int])->int:
minReach =[math.ceil(d / s)for d, s inzip(dist, speed)]
minReach.sort()
res =0for minute inrange(len(minReach)):if minute >= minReach[minute]:return res
res +=1return res
publicclassSolution{publicinteliminateMaximum(int[] dist,int[] speed){int n = dist.length;int[] minReach =newint[n];for(int i =0; i < n; i++){
minReach[i]=(int)Math.ceil((double) dist[i]/ speed[i]);}Arrays.sort(minReach);int res =0;for(int minute =0; minute < n; minute++){if(minute >= minReach[minute]){return res;}
res++;}return res;}}
classSolution{public:inteliminateMaximum(vector<int>& dist, vector<int>& speed){int n = dist.size();
vector<int>minReach(n);for(int i =0; i < n; i++){
minReach[i]=ceil((double)dist[i]/ speed[i]);}sort(minReach.begin(), minReach.end());int res =0;for(int minute =0; minute < n; minute++){if(minute >= minReach[minute]){return res;}
res++;}return res;}};
classSolution{/**
* @param {number[]} dist
* @param {number[]} speed
* @return {number}
*/eliminateMaximum(dist, speed){let n = dist.length;let minReach =newArray(n);for(let i =0; i < n; i++){
minReach[i]= Math.ceil(dist[i]/ speed[i]);}
minReach.sort((a, b)=> a - b);let res =0;for(let minute =0; minute < n; minute++){if(minute >= minReach[minute]){return res;}
res++;}return res;}}
publicclassSolution{publicintEliminateMaximum(int[] dist,int[] speed){int n = dist.Length;int[] minReach =newint[n];for(int i =0; i < n; i++){
minReach[i]=(int)Math.Ceiling((double)dist[i]/ speed[i]);}
Array.Sort(minReach);int res =0;for(int minute =0; minute < n; minute++){if(minute >= minReach[minute]){return res;}
res++;}return res;}}
funceliminateMaximum(dist []int, speed []int)int{
n :=len(dist)
minReach :=make([]int, n)for i :=0; i < n; i++{
minReach[i]=(dist[i]+ speed[i]-1)/ speed[i]}
sort.Ints(minReach)
res :=0for minute :=0; minute < n; minute++{if minute >= minReach[minute]{return res
}
res++}return res
}
class Solution {funeliminateMaximum(dist: IntArray, speed: IntArray): Int {val n = dist.size
val minReach =IntArray(n){ i ->(dist[i]+ speed[i]-1)/ speed[i]}
minReach.sort()var res =0for(minute in0 until n){if(minute >= minReach[minute]){return res
}
res++}return res
}}
classSolution{funceliminateMaximum(_ dist:[Int],_ speed:[Int])->Int{let n = dist.count
var minReach =[Int](repeating:0, count: n)for i in0..<n {
minReach[i]=(dist[i]+ speed[i]-1)/ speed[i]}
minReach.sort()var res =0for minute in0..<n {if minute >= minReach[minute]{return res
}
res +=1}return res
}}
Time & Space Complexity
Time complexity: O(nlogn)
Space complexity: O(n)
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.
Space complexity: O(1) or O(n) 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 = 0 to count eliminations.
While the heap is non-empty:
Pop the smallest arrival time.
If res >= arrival_time, the monster has reached the city. Return res.
classSolution:defeliminateMaximum(self, dist: List[int], speed: List[int])->int:
minHeap =[]for i inrange(len(dist)):
heapq.heappush(minHeap, dist[i]/ speed[i])
res =0while minHeap:if res >= heapq.heappop(minHeap):return res
res +=1return res
publicclassSolution{publicinteliminateMaximum(int[] dist,int[] speed){PriorityQueue<Double> minHeap =newPriorityQueue<>();for(int i =0; i < dist.length; i++){
minHeap.add((double) dist[i]/ speed[i]);}int res =0;while(!minHeap.isEmpty()){if(res >= minHeap.poll()){return res;}
res++;}return res;}}
classSolution{public:inteliminateMaximum(vector<int>& dist, vector<int>& speed){
priority_queue<double, vector<double>, greater<double>> minHeap;for(int i =0; i < dist.size(); i++){
minHeap.push((double)dist[i]/ speed[i]);}int res =0;while(!minHeap.empty()){if(res >= minHeap.top()){return res;}
minHeap.pop();
res++;}return res;}};
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.