当青训营遇上码上掘金——寻友之旅

109 阅读2分钟

当青训营遇上码上掘金

题目:主题三之寻友之路

小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式 可选:步行和公交。

步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)

问题:请帮助小青通知小码,小青最快到达时间是多久?

输入: 两个整数 N 和 K
输出: 小青到小码家所需的最短时间(以分钟为单位)

解题思路

已知小青行动的方式有两种,求通勤的最短时间,可以看作是寻找最短路径问题,一般可以考虑遍历的算法(如深/广度优先搜索),在这道题中,我们可以考虑使用动态规划的方法求最优解,动态规划和其它遍历算法都是将原问题拆成多个子问题然后求解,他们之间最本质的区别是,动态规划保存子问题的解,避免重复计算。解决动态规划问题的关键是找到状态转移方程,这样我们可以通过计算和储存子问题的解来求解最终问题。
在这道题目中,小青和小码的家在一条直线上,可以用一维的动态数组求解,我们定义一个数组dp,dp[i]表示从小青家也就是N点到小码家K点通勤的最小时间,小青出行有两种方式,步行,一分钟往前后移动一格,公交,一分钟移动两倍所在位置值的距离且不能后退,那么对于某一点 i:

  1. 首先如果他在小青家N点前面,公交不能后退,只能步行,那么最短时间就是N - i
  2. 如果在小青家N点后面,则可以坐公交:
    • 如果是偶数位置,那么它可以直接由前面的点坐公交到达时间最短,状态转移为dp[i] = dp[i/2] + 1;
    • 如果是奇数位置,那么它前后两格距离是偶数位置,状态转移为dp[i] = Min(dp[i-1] + 1, dp[(i+1)/2] + 2)。

从0开始遍历到K点停止,输出到达K点的最优解。
具体见下面代码实现。

代码实现(golang)

import "fmt"
func Min(x, y int) int {
    if x < y {
        return x
    }
    return y
}
func main() {
  var N int
  var K int
  fmt.Println("请输入K和N,用空格隔开,0 ≤ N, K ≤ 100000")
  fmt.Scanf("%d %d", &N, &K)
  dp := make([]int,K+1)
  for i := 0 ; i<K+1 ; i++{
    if i<=N{
      dp[i]=N-i
    }else{
      if i%2 == 0{
        dp[i] = dp[i/2]+1
      }else{
        dp[i] = Min(dp[(i+1)/2]+2, dp[i-1]+1)
      }
    }
  }
  fmt.Println("最短时间为:", dp[K])
}

go语言有Math包可以比较浮点数的大小,但不支持整数大小的比较,当然编写一个比较大小的函数很简单。
代码链接:https://code.juejin.cn/pen/7189588376809373754

最后测试结果如下:

捕获.PNG