「青训营 X 码上掘金」 主题 3:寻友之旅

75 阅读2分钟

当青训营遇上码上掘金

题目

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

题解

根据题目要求,我们可以先对小青的地点N进行分类:

  • 小青的地点N大于小码的地点K,即N > K

    那么由于小青在乘坐公交时不能向后出发,所以小青不能选择坐公交的方式了,只剩下通过x - 1步行到小码家。此时答案显然为N到K的直线距离,即:N - K

  • 小青的地点N小于小码的地点K,即N < K

    小青此时可以选择两种方式到达小青家,但是由于2 × X的方式显然更快,我们利用贪心法可以快速得到,小青首先应该选择乘坐公交。但是由于公交前进距离的特殊性,每次× 2,可以得到,在第i次乘坐公交后,其位置在 2 × Xi 处,此时,如果小青的位置如果已经大于或等于K,此时小青只能步行往回走,这显然可能会浪费一部分步数,除此之外,我们还可以选择在上一次公交后直接步行到小码家。那么我们可以比较此次之后小青到小码家需要的步数和小青i-1次后步行到小码家的距离,得到较小值即为答案。

  • 小青的地点N等于小码的地点K,即N = K

    显然,此时答案为0。

综上所述,我们可以写下以下代码:

(如果我们的go程序需要读取终端内容,那么我们可以使用fmt包的Scanln或者Scanf方法。两个方法会返回一个scan变量和err错误,我们除了查看是否报错外,需要注意传入的读取终端的变量须要使用指针,保证终端的内容可以顺利回传到变量上去。)

func main() {
    var N, K int
    _, err := fmt.Scanf("%d %d", &N, &K)
    if err != nil {
        return
    }
    // 将N > K和N = K 合并为一种情况
    switch N < K {
    case true:
        ans := 0
        for N < K {
            N *= 2
            ans++
        }
        // 比较i次后小青到小码家需要的步数和小青i-1次后步行到小码家的距离
        if ans+(N-K) < ans-1+(K-N/2) {
            fmt.Println(ans + (N - K))
        } else {
            fmt.Println(ans - 1 + (K - N/2))
        }
    case false:
        fmt.Println(N - K)
    }
}

项目执行如下: