制造焦虑:打破幻觉
最近 Google 的面试真题在各大求职群疯传。
很多人第一眼看到题目的反应是:「这不就是排序 + 模拟吗?秒了!」
大错特错。
如果你只是写个暴力模拟,恭喜你,面试官会微笑着追问你三个 Followup,然后你就 Rej 了。
我们 oavoservice 团队第一时间拿到了这道真题,深挖后发现里面藏着大量关于 Greedy Algorithm、Binary Search 和 Edge Case 的考察点。
题目拆解:展示专业
🔍 原题描述
You're on a remote island that needs evacuation due to pending flooding. The government is sending planes tomorrow to get some people out. If you miss the last plane you'd have to take a slower boat the following day which you don't want. Given the plane schedule and the schedule of all your fellow islanders, What is the latest you could get to the airport tomorrow and still get evacuated?
📝 中文翻译
你被困在一个即将被洪水淹没的偏远小岛上。
政府明天会派出多班飞机来撤离部分岛民。
如果你错过所有航班,只能第二天坐慢船离开(你肯定不想这样)。
已知:
- 所有航班的起飞时间和座位数
- 所有其他岛民计划到达机场的时间
规则:先到先上,座位有限
问题:你最晚什么时候到达机场,还能保证坐上飞机?
深度复盘:建立信任
🧠 题目本质分析
这道题表面是「调度问题」,实际考察的是:
| 考点 | 具体内容 |
|---|---|
| 贪心思维 | 如何分配乘客到航班? |
| 二分查找 | 如何高效找到临界时间点? |
| 边界处理 | 航班满员、无航班、时间冲突等 Edge Case |
| 复杂度优化 | 从 O(n²) 暴力到 O(n log n) 优化 |
📊 输入输出格式
输入:
- flights: List[(departure_time, capacity)] # 航班列表:(起飞时间, 座位数)
- arrivals: List[int] # 其他岛民到达机场的时间
输出:
- int: 你能保证上机的最晚到达时间
- 如果无论如何都上不了飞机,返回 -1
🎯 示例分析
Example 1:
flights = [(100, 2), (200, 1)] # 航班1: 100时刻起飞,2座位;航班2: 200时刻起飞,1座位
arrivals = [50, 80] # 两个岛民分别在50和80时刻到达
输出: 99
解释:
- 岛民A在50到达,岛民B在80到达
- 航班1(100起飞,2座位):岛民A和B都能上
- 如果你在99到达,你也能挤上航班1的最后一个... 等等,座位只有2个!
- 实际上:岛民A(50)和岛民B(80)占了航班1的2个座位
- 你只能去航班2(200起飞,1座位),所以你最晚199到达
等等,上面的分析对吗?让我们重新审视...
正确分析:
- 航班1(100起飞,2座位):在100之前到达的人可以上
- 岛民A(50)、岛民B(80) 都能上,占满2个座位
- 航班2(200起飞,1座位):在200之前到达的人可以上
- 没有其他人抢这班,你只要在199之前到就行
所以答案是 199
Example 2:
flights = [(100, 1)] # 只有一班航班,1个座位
arrivals = [50] # 一个岛民在50时刻到达
输出: 49
解释:
- 唯一的航班在100起飞,只有1个座位
- 岛民A在50到达,如果你在50之后到,座位就被抢了
- 所以你必须在49或更早到达
Example 3:
flights = [(100, 1)]
arrivals = [50, 60] # 两个岛民
输出: -1
解释:
- 只有1个座位,但有2个岛民比你先到(假设你不能比所有人都早)
- 无论你什么时候到,都没有座位了
- 返回 -1
方案引入:核心算法
思路一:暴力模拟 O(n × m × log m)
最直观的想法:枚举你的到达时间,模拟整个登机过程。
def latest_arrival_brute_force(flights, arrivals):
"""
暴力解法:枚举所有可能的到达时间
flights: List[(departure_time, capacity)]
arrivals: List[int] - 其他岛民的到达时间
"""
# 收集所有可能的时间点
all_times = set(arrivals)
for dep, _ in flights:
all_times.add(dep - 1) # 航班起飞前一刻
all_times.add(dep)
all_times = sorted(all_times, reverse=True) # 从晚到早枚举
for my_time in all_times:
if can_board(flights, arrivals, my_time):
return my_time
return -1
def can_board(flights, arrivals, my_time):
"""
检查在 my_time 到达时能否登机
"""
# 把自己加入到达列表
all_arrivals = sorted(arrivals + [my_time])
# 航班按起飞时间排序
sorted_flights = sorted(flights, key=lambda x: x[0])
# 贪心分配:每个人尽量坐最早能上的航班
flight_idx = 0
remaining_capacity = 0
current_departure = 0
for arrival in all_arrivals:
# 跳过已经起飞的航班
while flight_idx < len(sorted_flights) and sorted_flights[flight_idx][0] <= arrival:
if sorted_flights[flight_idx][0] > current_departure:
# 新航班加入
remaining_capacity += sorted_flights[flight_idx][1]
current_departure = sorted_flights[flight_idx][0]
flight_idx += 1
# 这里逻辑有问题,让我们重新设计...
return False # 占位
上面的代码有 Bug!让我们重新思考...
思路二:正确的贪心策略
核心洞察:
- 航班按起飞时间排序
- 乘客按到达时间排序
- 每个乘客贪心地选择「能上的最早航班」
- 二分查找你的最晚到达时间
def latest_arrival(flights, arrivals):
"""
正确解法:贪心 + 二分查找
flights: List[Tuple[int, int]] - (departure_time, capacity)
arrivals: List[int] - 其他岛民的到达时间
返回:你能保证上机的最晚到达时间,无法上机返回 -1
"""
if not flights:
return -1
# 按起飞时间排序航班
flights = sorted(flights, key=lambda x: x[0])
# 计算总容量
total_capacity = sum(cap for _, cap in flights)
# 如果总容量都不够所有人 + 你
if total_capacity <= len(arrivals):
return -1
# 二分查找最晚到达时间
# 搜索范围:0 到 最后一班航班起飞时间 - 1
left, right = 0, flights[-1][0] - 1
result = -1
while left <= right:
mid = (left + right) // 2
if can_evacuate(flights, arrivals, mid):
result = mid
left = mid + 1 # 尝试更晚的时间
else:
right = mid - 1 # 必须更早到
return result
def can_evacuate(flights, arrivals, my_arrival_time):
"""
判断在 my_arrival_time 到达时能否成功撤离
贪心策略:按到达顺序,每个人上「能上的最早航班」
"""
# 合并所有人的到达时间(包括自己)
all_arrivals = sorted(arrivals + [my_arrival_time])
# 为每个航班维护剩余座位
remaining = [cap for _, cap in flights]
for arrival in all_arrivals:
# 找到第一个「起飞时间 > 到达时间」且有座位的航班
boarded = False
for i, (dep, _) in enumerate(flights):
if dep > arrival and remaining[i] > 0:
remaining[i] -= 1
boarded = True
break
if not boarded:
# 这个人没上成飞机
if arrival == my_arrival_time:
return False # 是我没上成,失败
# 其他人没上成不影响判断(题目假设其他人一定会按计划到达)
return True
等等,上面的逻辑还有问题!
让我们再仔细读题:「先到先上」意味着到得早的人优先选择。但如果一个人到得早,他应该上哪班航班?
关键洞察:到得早的人应该上能上的最早航班,这样才不会浪费后面航班的座位给晚到的人。
思路三:最终正确版本
def latest_arrival_time(flights, arrivals):
"""
Google 面试真题:小岛撤离
Args:
flights: List[Tuple[int, int]] - (起飞时间, 座位数)
arrivals: List[int] - 其他岛民的到达时间
Returns:
int: 你能保证上机的最晚到达时间,-1 表示无法撤离
"""
if not flights:
return -1
# 按起飞时间排序
flights = sorted(flights, key=lambda x: x[0])
n_flights = len(flights)
# 总座位数
total_seats = sum(cap for _, cap in flights)
# 如果座位不够所有人(包括你)
if total_seats <= len(arrivals):
return -1
def simulate(my_time):
"""
模拟在 my_time 到达时能否登机
返回 True 表示能成功撤离
"""
# 所有人按到达时间排序
everyone = sorted(arrivals + [my_time])
# 每个航班的剩余座位
seats = [cap for _, cap in flights]
# 指向当前考虑的航班
flight_ptr = 0
for person_arrival in everyone:
# 找到第一个「起飞时间 > 到达时间」的航班
while flight_ptr < n_flights and flights[flight_ptr][0] <= person_arrival:
flight_ptr += 1
# 从 flight_ptr 开始找有座位的航班
boarded = False
for i in range(flight_ptr, n_flights):
if seats[i] > 0:
seats[i] -= 1
boarded = True
break
if not boarded and person_arrival == my_time:
return False
return True
# 二分查找最晚到达时间
# 最早可能是 0,最晚是最后一班起飞前一刻
lo, hi = 0, flights[-1][0] - 1
result = -1
while lo <= hi:
mid = (lo + hi) // 2
if simulate(mid):
result = mid
lo = mid + 1
else:
hi = mid - 1
return result
🧪 测试用例
# Test 1: 基本情况
flights1 = [(100, 2), (200, 1)]
arrivals1 = [50, 80]
print(latest_arrival_time(flights1, arrivals1)) # 预期: 199
# Test 2: 紧张情况
flights2 = [(100, 1)]
arrivals2 = [50]
print(latest_arrival_time(flights2, arrivals2)) # 预期: 49
# Test 3: 无法撤离
flights3 = [(100, 1)]
arrivals3 = [50, 60]
print(latest_arrival_time(flights3, arrivals3)) # 预期: -1
# Test 4: 多航班复杂情况
flights4 = [(50, 1), (100, 2), (150, 1)]
arrivals4 = [30, 40, 60, 90]
print(latest_arrival_time(flights4, arrivals4)) # 需要仔细分析
# Test 5: 边界 - 没有其他人
flights5 = [(100, 1)]
arrivals5 = []
print(latest_arrival_time(flights5, arrivals5)) # 预期: 99
# Test 6: 边界 - 没有航班
flights6 = []
arrivals6 = [50]
print(latest_arrival_time(flights6, arrivals6)) # 预期: -1
复杂度分析
| 方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 暴力枚举 | O(T × n × m) | O(n + m) |
| 二分 + 模拟 | O(log T × (n + m) log(n + m)) | O(n + m) |
其中:
- T = 最大时间范围
- n = 航班数量
- m = 其他岛民数量
🤯 面试官的 Followup 陷阱
Followup 1: 如果航班数量很大怎么办?
考点:优化模拟过程
当前解法每次模拟是 O((n + m) log(n + m)),如果 n 很大,可以用 线段树 或 树状数组 优化座位分配。
Followup 2: 如果要求返回你应该坐哪班航班?
def latest_arrival_with_flight(flights, arrivals):
"""返回 (最晚到达时间, 应该坐的航班索引)"""
# ... 在 simulate 函数中记录你上的是哪班航班
pass
Followup 3: 如果有多个「你」,都想尽量晚到?
考点:博弈论 / 多人优化
这就变成了一个更复杂的调度问题,需要考虑纳什均衡。
Followup 4: 如果航班可能延误呢?
考点:鲁棒性设计
需要考虑概率模型和风险评估。
🔥 为什么大多数人会挂?
| 常见错误 | 正确做法 |
|---|---|
| 只考虑「我能不能上」 | 需要模拟所有人的登机过程 |
| 暴力枚举所有时间点 | 二分查找优化 |
| 忽略「先到先上」规则 | 贪心策略:早到的人上早航班 |
| Edge Case 处理不当 | 无航班、满员、时间边界 |
📞 oavoservice 服务
这种场景建模 + 贪心 + 二分的组合题,是 Google 面试的经典风格。
如果你在面试中遇到类似题目,我们可以提供:
- ✅ OA代写:CodeSignal / HackerRank 满分保障
- ✅ VO辅助:Live Coding 实时场外助攻
- ✅ 思路指导:帮你建立正确的问题分解思维
👉 立即添加微信:Coding0201
不要让一道调度题,毁掉你的 Google Offer。
本文由 oavoservice 团队原创,转载请注明出处。