青训营刷题打卡(计算从位置 x 到 y 的最少步数,非循环解法) | 豆包MarsCode AI 刷题

74 阅读3分钟

计算从位置 x 到 y 的最少步数

问题描述

小F正在进行一个 AB 实验,需要从整数位置 x 移动到整数位置 y。每一步可以将当前位置增加或减少,且每步的增加或减少的值必须是连续的整数(即每步的移动范围是上一步的 -1+0 或 +1)。首末两步的步长必须是 1。求从 x 到 y 的最少步数。

输入描述

输入包含两个整数 x 和 y,表示起始位置和目标位置。

输出描述

输出从 x 到 y 所需的最小步数。


测试样例

样例1:

输入:x_position = 12, y_position = 6
输出:4

样例2:

输入:x_position = 34, y_position = 45
输出:6

样例3:

输入:x_position = 50, y_position = 30
输出:8

样例4:

输入:x_position = 0, y_position = 0
输出:0

问题分析

在刚看到这道题时,我遵循以往的做题经验,先想到了一个暴力的方法,那就是直接宽搜。队列中每次存储的数据为当前的下标,总共走的步数以及上一步走的长度。这样,每次搜索时只需根据上一次走的步长step与当前走到的位置,循环判断step+1+xstep+xstep+x-1是否有到达终点的地方,如果有那就直接返回当前总共走的步数+1即可。我们根据样例1进行操作。(为了方便展示,这里省略了一些数据。)

操作轮次当前队列中的元素到达每个地方的最小步数
0{ (12, 0, 0) }{ 12:0 }
1{ (11, 1, 1) ... }{ 12:0, 11: 1 ...}
2{ (10, 2, 1), (9, 2, 2) ... }{ 12:0, ..., 9:2 }
3{ (8, 3, 1), ... }{8:3 ... }
4{ (6, 4, 2), ... }{6:4, ... }

当找到目标位置时直接返回其步长即可。但是这种方法有较多弊端,比如如何存储每个位置的最小步数,去除队列中的冗余元素防止一直迭代不到目标位置。但总体来说,这种方法算是问题的一种解决方式。

进一步分析

那有没有一种可以直接求得最终结果得方法呢?我们再来看这个问题,并思考在搜索时的最优路径。显然我们在进行操作时与xy的初始具体位置并无关系,只需知道他们之间的距离即可。当前问题可以转换为:从0开始,走到|x-y|所需的最小步数。再来思考走法,显然,我们最终走的步长一定是1,2 ... n, n-1, ... 1 这种形式,这也是达到最终目的的最优路径。这里简单计算按照这种走法可以走到的位置。

1+2+...+n+(n1)+...+11+2+...+n+(n-1)+...+1 = n2n^2

那么,如果xy|x-y|是一个完全平方数时,可以直接得到结果为2n12n-1,其中n2=xyn^2 = |x-y|。但如果xy|x-y|不是一个完全平方数应该怎么办呢?显然我们可以在这2n12n-1步中再任意添加长度在[1,n][1,n]中的步长。即如果xyn2n|x-y|-n^2 \le n(其中n=xyn=\sqrt{|x-y|}), 时,我们可以用2n2n步走到最终的目的地。其实在这里,整个题目已经完成了大部分,只需要注意一点,如果xyn2>n|x-y| - n^2 > n时会怎样?这时候我们可以走两步来达到目的。首先,xyn2<2n+1|x-y| - n^2 < 2n + 1,原因也很简单,如果xyn22n+1|x-y| - n^2 \ge 2n + 1,那么xyn2+2n+1=(n+1)2|x-y| \ge n^2 + 2n + 1 = (n+1)^2,不满足我们的设定n=xyn=\sqrt{|x-y|}。所以,如果xyn2>n|x-y| - n^2 > n时,我们需要走的步数为2n+12n+1

这里给出Python的参考代码。

def solution(x_position, y_position):
    dis = abs(x_position - y_position)
    n = int(math.sqrt(dis))
    if dis == 0: return 0
    if n * n == dis: return 2 * n - 1
    else: return 2 * n + (1 if dis - n * n > n else 0) 

总结

遇到数学问题时,这类题目最怕推公式,我们一但推出具体公式,问题就能迎刃而解。