使用 Go 语言解决寻友之旅问题

96 阅读2分钟

当青训营遇上码上掘金

问题描述

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

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

这个问题的核心思路是动态规划。

关于动态规划

动态规划的思路是通过将一个复杂的问题分解成更简单的子问题的方式来简化它。它通常用于求解最优子结构的问题。

一般情况下,由于动态规划将复杂问题拆分成多个子问题计算,如果已经计算出其中一个子问题的解,那么往往会将其临时存储,这样可以使得其它相同的字问题可以通过查表的方式直接套用这个解,以此减少计算量。常用于求解最大或最小的数字,如求背包问题中物品总重量所能得到的最大价值;单词拆分问题中能以最少步数将单词拆分;公交路线问题中从一站走到另外一站的个数。

求解问题的思路

N 作为起点,K 作为终点。

K < N 时只能向左走,所以输出结果:N- K ;当 K > N 时:

line[i] 表示从N走到i时最少花费时间,如果当前 i 是偶数,那么 f[i] = min(f[i-1],f[i/2])+1;如果当前 i 是奇数,那么走到 i 一共有三种路径:

  1. i - 1 走到,那么时间是 f[i-1] + 1;
  2. i / 2 走到,例如走到 f[7] = f[3] + 1 + 1 (多加一步是因为2×3 = 6,再走一步才能到7);
  3. (i + 1) / 2 走到,同上,f[7] = f[4] + 1 + 1 (多加一步是因为2×4 = 8,再走一步才能到7)。

实现代码

package main

import (
	"fmt"
)

const M int = 1000010

var line [M]int

func min(a, b int) int {
	if a < b {
		return a
	} else {
		return b
	}
}

func main() {
	var N int = 0
	var K int = 0
	fmt.Scanf("%d%d", &N, &K)
	if N >= K {
		fmt.Println(N - K)
		return
	}
	line[N] = 0
	for i := 0; i <= N; i++ {
		line[i] = N - i
	}
	for i := N + 1; i <= K; i++ {
		if i%2 == 0 {
			line[i] = min(line[i-1], line[i/2]) + 1
		} else {
			line[i] = min(line[i-1]+1, min(line[i/2], line[(i+1)/2])+2)
		}
	}
	fmt.Println(line[K])
}