当青训营遇上码上掘金
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)
解题思路
由于题目给出的范围是 0≤N , K≤100 000 ,因此存在两种情况
当 N > K 时,由于公交只能向前移动,因此结果为 N - K;
当 N < K 时,有 +1 和 *2 两种方式移动,当 X*2 < K (X > 0)时,*2 花费的时间小于等于 +1 所需要的时间,当 X*2 > K (X > 0)时,需要比较 X*2 - K + 1 和 K - X 的大小,选择开销较小的方式。
同时,因为 X 需要大于 0,在比较时当 X==0 时,需要将 X +1 处理。
func main() {
var a, b int
fmt.Scan(&a, &b)
var cs = 0
var count = 0
if a >= b {
fmt.Println(a - b)
return
}
if a == 0 {
a += 1
count += 1
}
if a == b {
fmt.Println(count)
return
}
for {
if a*2 == b {
count += 1
break
} else if a*2 < b {
count += 1
cs += 1
a = a * 2
} else {
r := change(a*2-b, cs+1) + 1
r2 := change(b-a, cs)
if r > r2 {
count += r2
fmt.Println(b - a)
} else {
count += r
fmt.Println(r)
}
break
}
}
fmt.Println(count)
}
但是,通过简单的比较 X*2 - K + 1 和 K - X 的大小,并不能得到正确的最短时间,因为在 X 节点进行 +1 或 -1 的移动,比 X*2 节点更有效率。因此引入函数 change 通过对 X*2 - K + 1 和 K - X 的二进制数据进行比较,得到最简的移动方式,即可得到合理有效的答案。
// change函数 计算最简的 +1/-1 次数
// cs为 节点*2的次数
func change(num, cs int) int {
var count = 0
var result = 0
var std = int(math.Pow(2, float64(cs)))
var nums = num
var r0 = 0
var r1 = 0
var r1Count = 0
for num != 0 || count <= cs {
if num%2 == 0 && r1Count > r1 {
r0 += 1
r1 = r1Count
} else if num%2 == 1 {
r1Count += 1
}
num = num / 2
count += 1
}
r := 1 + r0 + r1Count - r1
if r < r1 {
result = r
} else {
result = r1Count
}
nums = (nums - std) / std
return result + nums
}