“字节”在数轴 0 点出发,向左或向右跳动(左右横跳),第一次跳 1 个单位,第二次 2 个单位,以此类推。给定系列目标
[x1, x2, ..., xn],“字节”依次跳动到给定目标,并在到达后重置跳跃步长,继续按 1, 2,... 步长跳往下一目标。求“字节”跳动到[x1, x2, ..., xn]的最小步数[s1, s2, ..., sn]
nums = [2, 1]3, 40 -> 1 -> -1 -> 2,步数为 32 -> 1,步数为 3 + 1nums = [1, 2, 2]1, 2, 20 -> 1,步数为 11 -> 2,步数为 1 + 12,步数为 21<=n<=100-10^7 <= xi <= 10^7以下是个人思路
从位置 x 跳到 y,新增步数只与间距 |x-y| 有关,问题转化为:给定间距 gap,求从 0 跳到 gap 的最小步数。用差分提取数据间距
gaps = [abs(nums[i+1]-nums[i]) for i in range(len(nums))]
gaps.insert(0, abs(nums[0])) # 补首元素假设 0 跳动到 n 的最小步数为 jump_to(n),累和得总步数 res
totalstep, res = 0, []
for gap in gaps:
totalstep += jump_to(gap)
res.append(totalstep)问题难点计算 jump_to(n),由于数据范围为 -10^7 <= xi <= 10^7,计算复杂度至多是线性的。
def jump_to(gap):
n = int((2 * gap) ** .5)
if n*(n + 1) < 2 * gap: # 定位上界
n += 1
if gap & 1: # 奇数情形
if n % 4 in (1, 2): return n
return n + (1 if n % 4 == 0 else 2)
if n % 4 in (0, 3): return n
return n + (1 if (n % 4) == 2 else 2)
def min_jump(nums):
gaps = [abs(nums[i+1] - nums[i]) for i in range(len(nums)-1)]
gaps.insert(0, abs(nums[0]))
totalstep, res = 0, []
for gap in gaps:
totalstep += jump_to(gap)
res.append(totalstep)
return res复杂度分析:
jump_to 的复杂度为 O(1)min_jump 时间复杂度为 O(N),空间复杂度优化后为 O(1)
def min_jump(nums):
pre, totalstep, res = 0, 0, []
for num in nums:
totalstep += jump_to(abs(num - pre))
res.append(totalstep)
pre = num
return res分析一:若
gap > 1+2+...+n,跳动到gap至少要n+1步
分析二:若n步到达gap,则gap = ± 1 ± 2 ± ... ± n,往左跳为负,往右为正
观察跳跃 6 次到达的位置:21, 19, 17,从 21 开始每次退 2,退到 15,步长减一

类似地,跳跃 5 次到达的位置:15, 13, 11, 9, 7, 5,从 15 开始退2, 退到 3,步长减一

注意后退不改变奇偶性,gap 的奇偶情形要分开处理
最后考虑累和序列 1, 1+2, 1+2+3,1+2+3+4,...,n*(n+1) 的奇偶性: 奇,奇,偶,偶,奇,奇,偶,偶...,即
n % 4 == 1, 2 序列值为奇数n % 4 == 0, 3 序列值为偶数算法思路:
n 使得 n*(n+1) >= 2*gapn 的奇偶性符合,返回 nn 的奇偶性不符合,返回最近的正确位置举两个例子:
gap | 2 * gap | 最小上界 n | 返回值 | 说明 |
|---|---|---|---|---|
8 | 16 <= 4 * 5 | n = 4 | 4 | gap 为偶数且 n % 4 ∈ {0, 3} |
14 | 28 <= 5 * 6 | n = 5 | 5+2 | gap 为偶数且 n % 4 ∉ {0, 3}, 余数+2 ∈ {0,3} |
具体地:
8 = -1 + 2 + 3 + 414 = 1 + 2 + 3 + 4 + 5 + 6 - 7