位置移动步数计算 | 豆包MarsCode AI 刷题

89 阅读4分钟

从 x 到 y 的最少步数问题解析与代码实现

问题描述

image.png

在这个问题中,我们被要求计算从一个整数位置 x 移动到另一个整数位置 y 所需的最少步数。每一步的移动可以是当前位置增加或减少一个连续的整数,即步长可以是 -1、0 或 +1。特别地,首末两步的步长必须是 1。这个问题实际上是一个动态规划问题,可以通过数学方法来解决。

输入输出描述

  • 输入:两个整数 x 和 y,分别表示起始位置和目标位置。
  • 输出:从 x 到 y 所需的最小步数。

测试样例

  1. 输入:x = 12, y = 6,输出:4
  2. 输入:x = 34, y = 45,输出:6
  3. 输入:x = 50, y = 30,输出:8
  4. 输入:x = 0, y = 0,输出:0

难点

其实我们显而易见可以得到这道题的难点在于a相关。

我们根本不用在意x y的具体大小,重点就是从0累加到diff即可。因为不需要求过程,只需要步数结果。

所以问题就从x->y变成了0->diff 相应的步长不变。

所以每次判定我们只需要判断这一步要不要累加,累加就不用管了。

如果不能累加就要判断需不需要减少。

let newAccu = 0;
const newA = a + 1;
// 判断能不能累加的条件就是 newA + newA-1 + ... + 1 > diff
for (let i = newA; i > 1; --i) {
  newAccu += i;
}

if (newAccu < diff - accu) {
  accu += 1;
  a += 1;
} else {
  newAccu -= (newA + a);
  // 如果不能累加 还要判断这一步骤是不是需要累减
  if (newAccu + 1 > diff - accu) {
    accu -= 1;
    a -= 1;
  }
}

在这段代码中,我们只需要先尝试累加,如果累加后的累计值比diff大肯定就不能累加。

至于累减判断也很简单,我们只需要判断1+2+...+a-1的值和diff-accu(即当前累计值)相比较即可。

举个例子:

整数位置 x12,另外一个整数位置 y6,我们需要从 x 走到 y,最小的步数为:1221,所以我们需要走 4 步。

diff显而易见可得为6

我们从1开始走,依次是:2(+1), 4(+2), 6(+2) 如果+3就超了

此时,我们的累计值是6,a是2 上一轮累计值是4+a(a = 2)-1=5然后 diff-accu=6-5=1 因为newAccu=a+1=3 此时3>1 所以必须累减

我们只需要将accu减少(因为是a-1 等同于累计值最终少加一)然后调整a即可。

这里也可以将a理解为accelerate,加速度,可能会更好理解?一个步长相当于调整g(嗯,可能)

算法分析

这个问题可以通过动态规划的思想来解决,但更简单的方法是使用数学公式。我们可以将问题分解为以下几个步骤:

  1. 计算绝对差值:首先计算 x 和 y 之间的绝对差值 diff,即 diff = Math.abs(x - y)

  2. 计算累加和:我们需要找到一个数 a,使得从 1 累加到 a 的和大于或等于 diff。这可以通过一个简单的循环实现,每次增加 a 的值,直到累加和大于或等于 diff。

  3. 判断是否需要调整:在找到 a 后,我们需要判断是否需要调整 a 的值。如果新的累加和(从 1 到 a+1)减去当前的累加和大于 diff 减去当前的 accu,那么我们不需要增加 a 的值。

  4. 计算步数:每次循环,我们都将步数 steps 加 1,直到 accu 等于 diff。

代码实现

以下是根据上述分析实现的 JavaScript 函数:

function solution(x, y) {
  let steps = 0;
  const diff = Math.abs(x - y);

  // 计算加到diff的最少步骤
  let a = 0, accu = 0;

  while (accu < diff) {
    accu += a;

    let newAccu = 0;
    const newA = a + 1;
    // 判断能不能累加的条件就是 newA + newA-1 + ... + 1 > diff
    for (let i = newA; i > 1; --i) {
      newAccu += i;
    }

    if (newAccu < diff - accu) {
      accu += 1;
      a += 1;
    } else {
      newAccu -= (newA + a);
      // 如果不能累加 还要判断这一步骤是不是需要累减
      if (newAccu + 1 > diff - accu) {
        accu -= 1;
        a -= 1;
      }
    }

    steps += 1;
  }

  return steps;
}

function main() {
  console.log(solution(12, 6) === 4);
  console.log(solution(34, 45) === 6);
  console.log(solution(50, 30) === 8);
  console.log(solution(0, 0) === 0);
}

main();

总结

这个问题是一个典型的动态规划问题,但通过数学方法可以更简单地解决。我们通过计算累加和来确定每一步的步长,并根据需要调整步长以确保首末两步的步长为 1。