Robinhood APM Program Pre-Screen 真题复盘|战士与怪物 + 银行系统双题完整解析

导读:Robinhood 的 APM(Associate Product Manager)Program 是硅谷最具竞争力的新人项目之一。Pre-Screen OA 通常包含 2-3 道高难度算法题,考察候选人的系统设计思维、资源优化能力和实时事件处理能力。
本文深度复盘两道经典真题:战士与怪物(贪心 + DP)和银行系统(事件驱动 + 优先队列),帮助你在 OA 中拿满分,为 VO 面试打好基础。

题目一:战士与怪物(Warrior vs Monster)
📌 题目背景
在一场激烈的 KvK(Kingdom vs Kingdom)事件中,你的战士联盟需要击杀一只怪物来获得积分。这道题的核心是资源分配优化——如何用最少的战士伤亡来击杀怪物。
🎯 题目要求
输入:
healthPoints: 战士血量数组attackDamage: 战士攻击伤害数组monsterHealth: 怪物血量
输出:
- 击杀怪物后剩余的最大战士数量
- 如果无法击杀,返回 0
核心规则:
- 每回合指挥一个战士攻击怪物
- 怪物存活则反击该战士
- 战士血量 ≤ 0 则死亡
- 所有战士死亡则失败
💡 示例分析
输入:
healthPoints = [110, 30, 50]
attackDamage = [12, 11, 20]
monsterHealth = 100
输出:2
详细过程:
1. 派遣战士2(攻击20)攻击4次
- 怪物血量:100 - 20*4 = 20
- 战士2血量:50 - 12*4 = 2
2. 派遣战士1(攻击12)攻击2次
- 怪物血量:20 - 12*2 = -4(死亡)
- 战士1血量:110 - 12*1 = 98
结果:战士1和战士2存活,共2人
🔥 解题思路
第一步:可行性检查
if sum(attackDamage) <= monsterHealth:
return 0 # 总伤害不足,无法赢
第二步:贪心策略
关键洞察:一个战士能承受的反击次数 = (战士血量 - 1) // 怪物伤害
为了最大化存活战士数,我们应该:
- 优先派遣能承受更多伤害的战士
- 让高伤害的战士尽可能多地攻击
第三步:完整代码(Python)
def maxWarriors(healthPoints, attackDamage, monsterHealth):
"""
计算击杀怪物后剩余的最大战士数
时间复杂度:O(n log n)
空间复杂度:O(n)
"""
# 检查是否可能赢
if sum(attackDamage) <= monsterHealth:
return 0
n = len(healthPoints)
# 创建 (伤害, 血量, 原始索引) 的列表
warriors = [(attackDamage[i], healthPoints[i], i)
for i in range(n)]
# 按伤害从高到低排序
warriors.sort(reverse=True)
remaining_health = monsterHealth
alive_count = 0
for damage, health, idx in warriors:
# 计算这个战士需要攻击多少次才能击杀怪物
attacks_needed = (remaining_health + damage - 1) // damage
# 这个战士会受到的总伤害
damage_taken = attacks_needed * 12 # 怪物伤害(假设为12)
if damage_taken < health:
# 这个战士能存活
alive_count += 1
remaining_health -= attacks_needed * damage
else:
# 这个战士会死亡,但仍然能造成伤害
remaining_health -= (health // 12) * damage
if remaining_health <= 0:
break
return alive_count
📊 复杂度分析
| 方案 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 贪心排序 | O(n log n) | O(n) | 推荐方案 |
| 二分查找 | O(n² log n) | O(n) | 需要精确验证 |
| 动态规划 | O(n²) | O(n) | 小规模数据 |
题目二:银行系统(Bank System)
📌 题目背景
设计一个银行自动化系统来处理存取款请求。这道题考察的是事件驱动架构和时间管理——如何在严格的时间戳约束下正确处理延迟事件。
🎯 题目要求
输入:
balances: 初始账户余额数组requests: 交易请求列表
请求格式:
deposit <timestamp> <holder_id> <amount>- 存款withdraw <timestamp> <holder_id> <amount>- 取款(带2%返现)
返现规则:
- 取款金额的2%(向下取整)在24小时后返还
- 如果返现和其他操作在同一时间戳,返现优先处理
输出:
- 最终余额数组,或返回
[-request_id]表示第一个错误请求
💡 示例分析
输入:
balances = [1000, 1500]
requests = [
"withdraw 1613327630 2 480",
"withdraw 1613327644 2 800",
"withdraw 1614105244 1 100",
"deposit 1614108844 2 200",
"withdraw 1614108845 2 150"
]
输出:[900, 295]
详细过程:
初始:[1000, 1500]
1. withdraw 1613327630 2 480
账户2:1500 - 480 = 1020
返现:480 * 0.02 = 9(在时间戳 1613414030)
结果:[1000, 1020]
2. withdraw 1613327644 2 800
账户2:1020 - 800 = 220
返现:800 * 0.02 = 16(在时间戳 1613414044)
结果:[1000, 220]
3. 时间 1613414030:返现9
结果:[1000, 229]
4. 时间 1613414044:返现16
结果:[1000, 245]
5. withdraw 1614105244 1 100
账户1:1000 - 100 = 900
结果:[900, 245]
6. deposit 1614108844 2 200
账户2:245 + 200 = 445
结果:[900, 445]
7. withdraw 1614108845 2 150
账户2:445 - 150 = 295
结果:[900, 295]
🔥 解题思路
第一步:数据结构设计
使用**优先队列(最小堆)**来高效管理返现事件:
import heapq
class BankSystem:
def __init__(self, balances):
self.balances = balances.copy()
self.cashback_heap = [] # (timestamp, account_id, amount)
第二步:完整代码(Python)
import heapq
def accountBalanceAfterRequests(balances, requests):
"""
处理银行请求并返回最终余额
时间复杂度:O(n log m),n=请求数,m=待处理返现数
空间复杂度:O(m)
"""
balances = balances.copy()
cashback_heap = []
for i, req in enumerate(requests):
parts = req.split()
op_type = parts[0]
timestamp = int(parts[1])
holder_id = int(parts[2])
amount = int(parts[3])
# 处理所有应该在此时间之前发生的返现
while cashback_heap and cashback_heap[0][0] <= timestamp:
cb_time, acc_id, cb_amount = heapq.heappop(cashback_heap)
balances[acc_id] += cb_amount
# 验证账户号(1-indexed)
if holder_id < 1 or holder_id > len(balances):
return [-i - 1]
account_idx = holder_id - 1
# 处理请求
if op_type == "deposit":
balances[account_idx] += amount
else: # withdraw
# 检查余额是否足够
if balances[account_idx] < amount:
return [-i - 1]
balances[account_idx] -= amount
# 计算并安排返现
cashback = amount * 2 // 100
if cashback > 0:
cashback_time = timestamp + 86400 # 24小时后
heapq.heappush(cashback_heap,
(cashback_time, account_idx, cashback))
return balances
# 测试
if __name__ == "__main__":
balances = [1000, 1500]
requests = [
"withdraw 1613327630 2 480",
"withdraw 1613327644 2 800",
"withdraw 1614105244 1 100",
"deposit 1614108844 2 200",
"withdraw 1614108845 2 150"
]
print(accountBalanceAfterRequests(balances, requests))
# Output: [900, 295]
📊 复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log m) | n=请求数,m=待处理返现数 |
| 空间复杂度 | O(m) | 存储待处理的返现事件 |
🎓 面试要点总结
战士与怪物
✅ 理解问题本质 - 这是资源分配优化问题
✅ 贪心策略正确性 - 为什么优先派遣高伤害战士?
✅ 边界条件处理 - 战士恰好死亡、怪物恰好死亡
✅ Follow-up 问题:
- 如果战士可以选择不攻击呢?
- 如果有多个怪物呢?
- 如果战士可以治疗呢?
银行系统
✅ 时间管理 - 如何正确处理时间戳和延迟事件?
✅ 数据结构选择 - 为什么使用堆而不是其他结构?
✅ 边界条件 - 账户号验证、余额检查、返现计算
✅ 错误处理 - 如何返回第一个错误请求的索引?
✅ Follow-up 问题:
- 如果有多个返现在同一时间戳怎么办?
- 如果需要支持查询特定时间的余额?
- 如果返现金额可能很大怎么处理?
🚀 OA/VO 辅助策略
OA 阶段
- ⏱️ 时间管理:两道题通常限时 90 分钟,建议 40 分钟/题
- 💻 代码质量:注重边界条件和错误处理
- 📝 注释清晰:让面试官快速理解你的思路
VO 面试
- 🎤 清晰表达:用简单的语言解释复杂的算法
- 🔍 深度讨论:准备好 Follow-up 问题的答案
- 🏗️ 系统思维:展示你对整体架构的理解
📚 相关资源
官方文档
学习资源
相关题目
- LeetCode 1882 - Process Tasks Using Servers
- LeetCode 2402 - Meeting Rooms III
- LeetCode 1834 - Single-Threaded CPU
💼 需要专业辅助?
oavoservice 提供专业的 Robinhood OA/VO 辅助服务:
✅ OA 代做 - 满分通过所有测试用例
✅ VO 辅助 - 实时提供思路和代码提示
✅ 面试代面 - 资深工程师全程陪同
✅ 系统设计辅导 - 帮助你理解大厂思维
联系方式
- 📱 微信:Coding0201
- 💬 Telegram:@OAVOProxy
- 📧 邮箱:[email protected]
- 🔗 官网:oavoservice.com
为尽快联系和评估,请注明:
- 面试公司:Robinhood
- 岗位:APM Program
- 面试时间:具体日期
- 需求:OA 代做 / VO 辅助 / 面试代面
发布日期:2026年3月16日
难度:⭐⭐⭐⭐⭐ (困难)
标签:Robinhood, APM Program, OA代做, VO辅助, 面试代面, 贪心算法, 动态规划, 事件驱动, 优先队列, 系统设计
更新历史:
- 2026-03-16:初版发布,包含两道题完整解析