高效求解轮盘弹跳路径:基于循环检测的 O(n) 时间复杂度优化方案

5次阅读

当弹跳次数 b 高达 10¹² 时,朴素模拟会超时;本文介绍通过检测状态序列的「起始段(prefix)」与「循环节(cycle)」,将时间复杂度从 o(b) 降至 o(n),完美应对大规模查询。

当弹跳次数 b 高达 10¹² 时,朴素模拟会超时;本文介绍通过检测状态序列的「起始段(prefix)」与「循环节(cycle)」,将时间复杂度从 o(b) 降至 o(n),完美应对大规模查询。

在 Hackerrank 的 Movement Slots 问题中,一个球从初始槽位 a 出发,在由 n 个槽位组成的环形轮盘上按规则弹跳 b 次:若当前位于槽位 i,则下一次落点为 (i + jumps[i]) % n(负数自动模运算支持逆时针)。朴素实现使用 for _ in range(b) 循环模拟,时间复杂度为 O(b),面对 b ≤ 10¹² 的极端输入必然超时。

根本优化思路是:状态空间有限(仅 n 个不同槽位),因此球的位置序列必在有限步内进入循环。该序列为典型的「ρ 形序列」(rho-shaped sequence):先经过一段非重复的起始路径(prefix),长度记为 X;随后进入长度为 Y 的纯循环(cycle),即存在最小正整数 Y,使得对所有 k ≥ X,有 pos[k + Y] == pos[k]。

由此可将计算拆解为三步:

  1. 检测循环结构:使用 Floyd 判圈法(快慢指针)或哈希表记录首次访问位置,获取 X(入环前步数)和 Y(环长);
  2. 分类计算
    • 若 b ≤ X:直接模拟 b 步;
    • 若 b > X:只需模拟 X + (b − X) % Y 步(跳过完整循环次数);
  3. 批量查询优化:对每个查询 (a, b) 独立执行上述逻辑;若 n 较小而 q 较大,可预处理每个起点 a 对应的 X_a, Y_a, cycle_a 数组,实现均摊 O(1) 查询。

以下是完整、健壮的 Python 实现(含循环检测与边界处理):

def detect_cycle_start_and_length(jumps, start):     n = len(jumps)     # 使用字典记录每个位置首次出现的步数     visited = {}     current = start     step = 0      while current not in visited:         visited[current] = step         current = (current + jumps[current]) % n         step += 1      X = visited[current]  # 入环点步数(起始段长度)     Y = step - X           # 循环节长度     return X, Y, visited  def final_slot_optimized(a, b, jumps):     if b == 0:         return a     n = len(jumps)     X, Y, visited = detect_cycle_start_and_length(jumps, a)      if b <= X:         # 未入环,直接模拟         pos = a         for _ in range(b):             pos = (pos + jumps[pos]) % n         return pos     else:         # 跳过完整循环:只模拟 X + 剩余偏移         remaining = (b - X) % Y         pos = a         # 模拟前 X 步到达环入口         for _ in range(X):             pos = (pos + jumps[pos]) % n         # 再模拟 remaining 步(在环内)         for _ in range(remaining):             pos = (pos + jumps[pos]) % n         return pos  # 批量查询主函数(适配原题输入格式) def find_last_bounce(jumps, queries):     results = []     for a, b in queries:         results.append(final_slot_optimized(a, b, jumps))     return results

关键优势

  • 单次查询最坏时间复杂度为 O(n)(循环检测 + 最多 X + Y ≤ 2n 步模拟),与 b 无关;
  • 空间复杂度 O(n),用于存储访问记录;
  • 完全兼容负向跳跃(Python 的 % 对负数自动归一化到 [0, n−1]);
  • 无需额外图论建模,逻辑清晰、易于调试。

⚠️ 注意事项

  • jumps[i] 可为负数,务必使用 (i + jumps[i]) % n 而非 (i + jumps[i]) % n 的错误变体(如 + n) % n 更安全,但 Python % 已保证结果非负);
  • 若 n = 0 需提前校验(题目保证 n ≥ 1);
  • Floyd 法可将空间降至 O(1),但哈希表法更直观且 n 通常 ≤ 10⁵,内存友好。

总结:面对隐式状态图上的超长路径查询,识别并利用「有限状态 ⇒ 必然循环」这一本质特性,是解决此类问题的通用范式。本方案不仅通过循环压缩将 b = 10¹² 降维至 ≤ 2×10⁵ 步内完成,更为同类问题(如链表环检测、模幂周期、状态机长期演化)提供了可迁移的方法论。

text=ZqhQzanResources