【算法题解】保护城市:怪物消灭最大数量问题详解
题目描述
你正在玩一款电子游戏,目标是保护城市不被怪物侵袭。
游戏设定如下:
- 有
n
个怪物,第i
个怪物与城市的初始距离是dist[i]
(单位:千米)。 - 每个怪物以恒定速度向城市前进,速度为
speed[i]
(单位:千米/分钟)。 - 武器在游戏开始时是充满电的,可以立即消灭一个怪物,但之后每次消灭怪物后需要 1 分钟充电。
- 怪物从第 0 分钟开始移动。
- 如果在任意一分钟的开始时,任何怪物恰好到达城市(距离为0),游戏立即失败。
- 目标是计算在游戏失败前,最多能消灭多少怪物。
问题理解与分析
关键在于怪物的到达时间和你的攻击节奏:
- 怪物到达时间计算:第
i
个怪物的到达时间是ceil(dist[i] / speed[i])
分钟。 - 攻击时间线:你从第 0 分钟开始,每分钟可以消灭一个怪物(武器开始时已充满电,消灭第一个怪物无需等待)。
- 胜利条件:在第
k
分钟消灭第k
个怪物之前,该怪物必须还未抵达城市,即time[k] > k
。 - 一旦有怪物的到达时间小于等于它应该被消灭的时间,意味着该怪物已到达城市,游戏失败。
解题思路
计算所有怪物到达城市的时间:
对每个怪物,计算它到达城市的时间(分钟数):
- t i m e [ i ] = ⌈ d i s t [ i ] s p e e d [ i ] ⌉ time[i] = \lceil \frac{dist[i]}{speed[i]} \rceil time[i]=⌈speed[i]dist[i]⌉
- 排序怪物按到达时间升序:
先消灭那些最快到达的怪物。
- 逐个判断是否可以按时消灭:
遍历排序后的时间数组,如果在第 i
分钟,第 i
个怪物的到达时间 time[i]
小于或等于 i
,则游戏失败。
- 统计能消灭的怪物数量:
返回满足条件的最大索引 i
。
代码实现
import math
from typing import List
class Solution:
def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int:
# 计算所有怪物到达时间
time = [math.ceil(d / s) for d, s in zip(dist, speed)]
# 按到达时间升序排序
time.sort()
# 遍历判断是否可以按时消灭
for i, t in enumerate(time):
if t <= i:
return i # 不能消灭第 i 个怪物,返回前面消灭的数量
return len(dist) # 所有怪物都能消灭
时间与空间复杂度分析
- 时间复杂度:排序时间是 O(n log n),计算时间是 O(n),整体为 O(n log n)。
- 空间复杂度:存储时间数组需要 O(n) 额外空间。
这已经是最优的解决方案,因为必须排序怪物到达时间。
示例说明
示例 1
dist = [1, 3, 4]
speed = [1, 1, 1]
输出:3
过程说明:
- 到达时间分别为
[1, 3, 4]
分钟。 - 按顺序消灭:
- 第 0 分钟消灭距离1的怪物
- 第 1 分钟消灭距离3的怪物(剩余距离2)
- 第 2 分钟充电,怪物还没到达
- 第 3 分钟消灭距离4的怪物(剩余距离1)
- 所有怪物都可以被消灭,返回 3。
示例 2
dist = [1, 1, 2, 3]
speed = [1, 1, 1, 1]
输出:1
过程说明:
- 到达时间为
[1, 1, 2, 3]
- 按时间排序仍是
[1, 1, 2, 3]
- 第 0 分钟消灭第一个怪物
- 第 1 分钟时第二个怪物距离0,游戏失败
- 只能消灭 1 个怪物。
示例 3
dist = [3, 2, 4]
speed = [5, 3, 2]
输出:1
过程说明:
- 计算到达时间:
- 怪物0: ceil(3/5) = 1
- 怪物1: ceil(2/3) = 1
- 怪物2: ceil(4/2) = 2
- 排序到达时间为
[1, 1, 2]
- 第 0 分钟消灭第一个怪物(到达时间1)
- 第 1 分钟时,第二个怪物距离0,游戏失败
- 只能消灭 1 个怪物。
总结
这道题用一个很经典的“贪心排序”思路:
- 先算出怪物到达时间
- 按时间顺序消灭怪物
- 在消灭顺序上检测是否怪物已经到达城市
只要在每一步判断条件满足即可,简单明了且效率高。
如果你在游戏设计或类似的场景中需要处理“有限资源按顺序处理多事件”的问题,这种思路非常有参考价值。