创作灵感和过程| 码上掘金

146 阅读3分钟

题目:寻友之旅

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

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

方法一:广度优先算法(BFS)

思路:

定义一个结构体节点包含小青所在位置以及到达该位置所需最短时间,根据题意,每次可以向左、右、或者乘公交到达下一个节点。用一个队列存储待访问的节点,循环遍历每次取出队首的节点并将该节点能到达的其他节点加入队尾,直到找到小码所在节点为止。

代码实现:

  1. 定义一个结构体Node,包含当前位置cur和到达该位置所需最短时间step;
  2. 定义一个队列queue,将起点节点加入队列;
  3. 定义一个visited数组,用来记录每个节点是否被访问过,初值为false;
  4. 利用while循环,每次取出队首节点,扩展出其可达的节点,将可达节点加入队列;
  5. 判断加入队列的节点是否为小码所在节点,如果是,则返回该节点的到达时间step;
  6. 如果队列为空仍未找到小码所在节点,说明无法到达小码家,返回-1。
package main

func main() {
   minTimeToGoToK(10, 60)
}

type Node struct {
   cur, step int
}

func minTimeToGoToK(N, K int) int {
   if N == K {
      return 0
   }
   queue := []Node{{N, 0}}
   visited := make([]bool, 100001)
   visited[N] = true
   for len(queue) > 0 {
      initial := queue[0]
      queue = queue[1:]
      for _, next := range []Node{{initial.cur - 1, initial.step + 1}, {initial.cur + 1, initial.step + 1}, {initial.cur * 2, initial.step + 1}} {
         if next.cur == K {
            return next.step
         }
         if next.cur >= 0 && next.cur <= 100000 && !visited[next.cur] {
            visited[next.cur] = true
            queue = append(queue, next)
         }
      }
   }
   return -1
}

分析:

广度优先会遍历每个位置最多一次,因此时间复杂度为O(N),这里N小于等于100000。空间复杂度由于使用了队列queue和visited数组,因此空间复杂度也是O(N)。

方法二:动态规划

思路:

采用动态规划算法,从小青所在位置开始,计算到每个位置所需的最短时间,最终得到到小码所在位置所需的最短时间。

实现步骤:

  1. 定义一个长度为K+1的数组dp,dp[i]表示从小青所在位置到位置i所需的最短时间。
  2. 初始化dp数组,dp[N]=0,dp[i]=abs(i-N)。
  3. 从小青所在位置向右遍历,对于位置i,如果i是j的两倍,那么dp[i]=min(dp[i], dp[j]+1);否则dp[i]=min(dp[i], dp[j]+abs(i-j)),其中j为i的前一个位置。
  4. 从小青所在位置向左遍历,对于位置i,如果i是j的两倍,那么dp[i]=min(dp[i], dp[j]+1);否则dp[i]=min(dp[i], dp[j]+abs(i-j)),其中j为i的后一个位置。
  5. 返回dp[K],即小青到小码家所需的最短时间。

分析:

动态规划需要遍历K个位置,每个位置最多计算logK次,所以时间复杂度是O(KlogK);需要一个长度为K+1的数组来存储计算结果,所以空间复杂度是O(K)。