当青训营遇上码上掘金

37 阅读2分钟

当青训营遇上码上掘金

主题 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 + 1K - 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 + 1K - X 的大小,并不能得到正确的最短时间,因为在 X 节点进行 +1-1 的移动,比 X*2 节点更有效率。因此引入函数 change 通过对 X*2 - K + 1K - 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
}