You are standing at position 0
on an infinite number line. There is a goal at position target
.
On each move, you can either go left or right. During the n-th move (starting from 1), you take n steps.
Return the minimum number of steps required to reach the destination.
Example 1:
Input: target = 3
Output: 2
Explanation:
On the first move we step from 0 to 1.
On the second step we step from 1 to 3.
Example 2:
Input: target = 2
Output: 3
Explanation:
On the first move we step from 0 to 1.
On the second move we step from 1 to -1.
On the third move we step from -1 to 2.
Note:
target
will be a non-zero integer in the range [-10^9, 10^9]
.题目:在整数数轴上,求从初始位置为0到目标位置T的最小步数,规定第i步可以向左或向右移动,移动距离为i。
实现思路:参考资料给出的思路很清晰,这里只做一些整理以及说明。
根据题目要求,第i步的实际移动距离可以表示为 di x i,这里 di 表示方向(取值为 ± 1,正值表示向右移动,负值表示向左移动),因此到达目标位置T等价于实现每一步实际移动距离之和为T。如果在第N步抵达目标T,则有 T = d1 x 1 + d2 x 2 + ... + dN x N,看到这个式子很容易联想到等差数列公式(就是所有 di = 1的情况)。
性质1:将上式左右同乘-1,可知 - T = (- d1) x 1 + (- d2) x 2 + ... + (- dN) x N,这就说明到达目标位置T或者-T所用的最小步数是相同的,只需要将到达+T过程中每一步的方向取反即可。根据此对称性,我们考察T为正整数的情况即可。
前面说到T的式子类似等差数列公式(差异只在每一项的符号,di = ±1),因此根据等差数列求和公式,可知 1 + 2 + ... + N = (N + 1) x N / 2 >= T。
性质2.1: 如果等号成立,那么求解关于n的二元一次方程(N + 1) x N / 2 = T 就是答案,即 N = - 1 + 0.5 x (1 + 8T) ^ (1 / 2),此值需向上取整,这里舍掉一个负根。
如果等号不成立,我们看看“一路无脑向右移动”超过了目标位置多少距离,即 Δ = A - T,这里A = (N + 1) x N / 2。“走超了”说明某一些步骤要调整方向(从+1改为-1),那么假如固定第1到第N-1步的方向不变,只调整第N步(将方向由+1改为-1),则有 B = 1 + 2 + ... + (N - 1) - N。可见,调整方向后,累计距离减少了2N,也就是对我们的“无脑向右”方案修正了2N。对于Δ来说,我们可能需要调整很多步才能满足,因此有 2i + 2j + 2p + 2q + ... = 2 x (i + j + p + q + ...) = Δ,这里i, j, p, q表示调整第i, j, p, q步的方向。这个式子说明当Δ是偶数时,我们只需要在原来“无脑方案”上进行有限的修正并且不改变总步数就能满足要求(题目没有要求我们求出整个行进过程,因此不用纠结具体哪一步调整方向)。这里啰嗦一下,Δ是正整数且i, j, p, q也是正整数,显然一个正整数Δ可以表示为任意正整数之和。
如果Δ为奇数,那么我们只需要“再走几步”让它变成偶数就OK了。现在考虑“无脑向右”多走一步,此时Δ* = Δ + (N + 1),由于奇数+奇数=偶数、奇数+偶数=奇数的性质,当N为偶数时,Δ*为偶数,根据上面的分析可知第N + 1步能实现目标。当N为奇数时,Δ*为奇数,此时需要再“无脑向右”多走一步,得到Δ** = Δ* + (N + 2),可知Δ**为偶数,即第N + 2步能实现目标。
整理一下,得到如下性质:
性质2.2: 如果等号不成立,求 Δ = (N + 1) x N / 2 - T。如果Δ为偶数则结果为N, 如果Δ为奇数且N为偶数则结果为N + 1,如果Δ为奇数且N为奇数则结果为N + 2。
在实现时,考虑到8 * N可能会溢出,不能先估算N的值,改为一步一步运算。具体来说,每一步都先“无脑向右”移动,直到累计总和不小于目标值,然后检查Δ,即对于第 k 步如果Δ = 0则返回k,否则应用性质2.2即可。这里注意判断奇偶性的方法,不要用x % 2 == 1判断奇数,因为负奇数与2取余的结果是-1。
class Solution {
public int reachNumber(int target) {
int positive_target = Math.abs(target);
int steps = 0, sum = 0;
while (sum < positive_target) {
steps += 1;
sum += steps;
}
int delta = sum - positive_target;
if (delta == 0 || delta % 2 == 0)
return steps;
return steps % 2 == 0 ? steps + 1 : steps + 2;
}
}
参考资料:https://leetcode.com/problems/reach-a-number/discuss/112968/Short-JAVA-Solution-with-Explanation