当青训营遇上码上掘金 | 主题3:寻友之旅

115 阅读3分钟

当青训营遇上码上掘金

当青训营遇上码上掘金 | 主题3:寻友之旅

题目描述

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

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

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

输入格式

输入包含多组测试数据,每组数据占一行,包括两个整数 N 和 K,以空格分隔

输出格式

每组数据输出一行结果,是一个整数,表示小青到小码家所需的最短时间(以分钟为单位)

数据范围

0≤N , K≤100 000。

输入样例

5 17

9 5

0 15

3 3

输出样例

4

4

6

0

解题思路

本题可使用动态规划算法求解,具体分析思路如下。

  • 状态表示:f[i]
    • 集合:所有从Ni的移动路径
    • 属性:min,路径耗时的最小值
  • 状态计算:集合划分
    • 0<=i<=N时,由于公交不可以向左走,所以只有步行一种交通方式可选,即只能向左步行N-i步,因此f[i] = N-i
    • i>N
      • i是偶数时
        • 所有从Ni的移动路径:f[i]
          • 最后一步是从左边向右步行过来的路径:f[i-1]+1
          • 最后一步是从左边向右乘公交过来的路径:f[i/2]+1
        • 转移方程
          • f[i] = min(f[i-1]+1, f[i/2]+1)
      • i是奇数时
        • 所有从Ni的移动路径:f[i]
          • 最后一步是从左边向右步行过来的路径:f[i-1]+1
          • 最后两步是先乘公交到i+1再向左步行过来的路径:f[(i+1)/2]+2
          • 最后两步是先乘公交到i-1再向右步行过来的路径:f[(i-1)/2]+2
        • 转移方程
          • f[i] = min(f[i-1]+1, f[(i+1)/2]+2, f[(i-1)/2]+2)
  • 具体实现

完整代码

使用Go语言实现上述动态规划算法,完整代码如下,也可参见链接:当青训营遇上码上掘金 | 主题3:寻友之旅 - 码上掘金 (juejin.cn)

package main

import (
	"fmt"
	"io"
)

const MAXN = 100010

var N int
var K int
var f [MAXN]int

func myMin(vars ...int) int {
	ret := vars[0]
	for _, i := range vars {
		if ret > i {
			ret = i
		}
	}
	return ret
}

func main() {
	for {
		_, err := fmt.Scanf("%d %d", &N, &K)
		if err == io.EOF {
			break
		}
		if K <= N {
			fmt.Printf("%d\n", N-K) // 只能向左步行
		} else { // K>N
			for i := 0; i <= N; i++ {
				f[i] = N - i // 只能向左步行
			}
			for i := N + 1; i <= K; i++ {
				if i%2 == 0 { // 最后一步是从左边向右步行过来、最后一步是从左边向右乘公交过来
					f[i] = myMin(f[i-1]+1, f[i/2]+1)
				} else { // 最后一步是从左边向右步行过来、最后两步是先乘公交到i+1再向左步行过来、最后两步是先乘公交到i-1再向右步行过来
					f[i] = myMin(f[i-1]+1, f[(i+1)/2]+2, f[(i-1)/2]+2)
				}
			}
			fmt.Printf("%d\n", f[K]) // 输出从起点N走到终点K所需的最短时间
		}
	}
}