青训营创作活动-主题三寻友之旅

61 阅读2分钟

当青训营遇上码上掘金

主题介绍

我选择的是主题三:寻友之旅

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)

请帮助小青通知小码,小青最快到达时间是多久?
输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)

题解

根据题意分析,对于小青而言,他可以对自己当前所在的位置做“+1”,“-1”或“*2”这三种操作。 我采用动态规划的方法解决本题。

  • 当N>=K时,显然可以直接返回N-K作为答案。
  • 当N<K时,对每一个当前所在位置x,我们可以得出当前位置的递推公式
    • 当x为奇数时,dp[x] = dp[x-1] + 1;
    • 当x为偶数时,dp[x] = 1 + min(dp[x-1], dp[x/2])

并且仅会存在,当小青在坐公交时超过了小码所在位置时,小青会向回走;其余情况下均不会向回走。 因而可以给出如下代码:

package main

import "fmt"

func main() {
    var N, K int
    N, K = 0, 98
    //fmt.Scanf("%d, %d", &N, &K)
    if N >= K {
      fmt.Printf("%d", N - K)
    } else {
      var dp [200005] int
      for i := N; i >= 0; i-- {
        dp[i] = N - i;
      }
      for i := N + 1; i <= 2 * K; i++ {
        if i % 2 == 1 {
          dp[i] = dp[i - 1] + 1
        } else {
          if dp[i - 1] < dp[i / 2] {
            dp[i] = dp[i - 1] + 1
          } else {
            dp[i] = dp[i / 2] + 1
          }
        }
        //fmt.Printf("%d %d\n", i, dp[i])
      }
      var now_min, now int
      now_min, now = dp[2 * K], 2 * K
      for i := 2 * K - 1; i >= K; i-- {
        if (dp[i] > now_min + now - i) {
          dp[i] = now_min + now - i
        }
        now_min, now = dp[i], i
      }
      fmt.Printf("%d", dp[K])
    }
}

仔细观察代码,我们可以发现,可以在从N遍历到K的同时,对向回走的情况同时进行处理。

package main

import "fmt"

func min(a ...int) int {
  min := 1000000

  for _, i := range a {
    if i < min {
      min = i
    }
  }
  return min
}
func main() {
    var N, K int
    N, K = 0, 100
    //fmt.Scanf("%d, %d", &N, &K)
    if N >= K {
      fmt.Printf("%d", N - K)
    } else {
      var dp [200005] int
      dp[K] = 1000000
      for i := 0; i <= K; i++ {
        if i != K {
          dp[i] = 1000000
        }
        if i <= N {
          dp[i] = min(N - i, dp[i])
        } else {
          if i % 2 == 1 {
            dp[i] = min(dp[i - 1] + 1, dp[i])
          } else {
            dp[i] = min(dp[i], dp[i - 1] + 1, dp[i / 2] + 1)
          }
        }
        num := i * 2 - K
        if num < 0 {
          num = num * -1
        }
        dp[K] = min(num + dp[i] + 1, dp[K])
      }
      fmt.Printf("%d", dp[K])
    }
}

时间复杂度为O(K)O(K)

下面是码上掘金的代码,如有错误欢迎指正。