在一根无限长的数轴上,你站在0的位置。终点在target的位置。你可以做一些数量的移动 numMoves :
- 每次你可以选择向左或向右移动。
- 第 i 次移动(从 i == 1 开始,到 i == numMoves ),在选择的方向上走 i 步。
给定整数 target ,返回 到达目标所需的 最小 移动次数(即最小 numMoves ) 。
思路
- 假设第1…, i 次均向右移动,那么移动的步数分别为1,2,3,…,i步;
- 假设第1…, i 次均向左移动,那么移动的步数分别为-1,-2,-3,…,-i步;
- 因此第1…, i 次的某次选择向左或向右移动,移动的总步数的差值分别为-2,-4,-6,…,-2i步;
- 假设移动到终点target位置,最多需要target步,那么可以根据二分迭代搜索需要的最小移动次数;
- 搜索起始边界L,R分别为0,target,对应的每次搜索的中间位置m=(L+R)/ 2, 搜索停止条件L > R,定义到达target位置附近所需要的移动次数为 near;
- 从1一直加到m的和,设为sum(m);如果sum(m) >= target,则更新near = m,直到停止搜索为止;
- 如果此时的sum(near) 恰好等于target,则near就是要找的最小移动次数;
- 如果此时的sun(near) > target,也即移动的总步数超出了target,建设此时超出的步数为diff = sun(near) - target
- 如果diff为偶数,也即需要之前的某一步向左移动即可满足(参考第三条差值),那么可以直接返回near。
- 如果diff为奇数,就需要near再向右移动,因为无论如何调整之前的某一步方向,不会改变sum(near)的奇偶性,也就不会改变diff的奇偶性,也就到不了target位置;
- 那near再向右移动多少步呢?好问题。观察一下sum(m)的数值,【1,3,6,10,15,21,28,36】奇奇偶偶奇奇偶偶 交替出现,因此最多再移动两步就可以改变sum(near)的奇偶性,也即改变了diff的奇偶性。
代码
Python
class Solution:
def sum(self, m):
return int((m * (m+1))/2)
def reachNumber(self, target: int) -> int:
target = abs(target)
l, r = 0, target
m, near = 0, 0
while l <= r:
m = int((l + r)/2)
if self.sum(m) >= target:
near = m
r = m - 1
else:
l = m + 1
if self.sum(near) == target:
return near
# 差值为奇数 sum和规律 奇奇偶偶奇奇偶偶 交替,因此循环两次
if ((self.sum(near) - target) & 1) == 1:
near += 1
if ((self.sum(near) - target) & 1) == 1:
near += 1
return near
Java
public class ReachNumber {
public static int reachNumber(long target) {
if (target == 0) {
return 0;
}
target = Math.abs(target);
long l = 0;
long r = target;
long m = 0;
long near = 0;
while (l <= r) {
m = (l + r) / 2;
if (sum(m) >= target) {
near = m;
r = m - 1;
} else {
l = m + 1;
}
}
if (sum(near) == target) {
return (int)near;
}
if (((sum(near) - target) & 1) == 1) {
near++;
}
if (((sum(near) - target) & 1) == 1) {
near++;
}
return (int)near;
}
public static long sum(long n) {
return (n * (n + 1)) / 2;
}
}