寻友之旅 | 青训营笔记

102 阅读1分钟

这是我参与「第五届青训营 」笔记创作活动的第七天,今天主要完成码上掘金活动的内容~

题意

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

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

思路

首先看数据范围,可知大概是O(n)或O(nlogn)的算法

然后简单看看题意,相当于一个节点最多有三种走法,要求走到目标点的最短路,可以考虑DP或搜索

发现每个点只要记录最早到达时间即可往后做贡献,且三种走法步长均为1,显然bfs好写又契合,dp还要考虑更多中途往回走的转移,故使用bfs

为了更契合平时习惯,以下称起点为S,目标点为T,往前为数轴正方向

中途考虑一个简单的特判,T <= S时只能一直往后走,故答案为S-T

考虑bfs细节,直接类似图的bfs遍历即可,只需要一个队列与一个深度(距离)数组。类似图,即:N = 节点数 = T * 2(最多可能先翻倍再往回走,虽然很蠢但能去掉很多特判,不需要节约这点空间),M = 转移数 = 3 * N故时空复杂度均为O(n)

(如果不懂图的bfs遍历的,简单来说就是先到先得,从初始点出发一层层染色,即标记已经搜过的地方,每次往没染过的相邻的地方染)

代码(GO版本)

//s-起点,t-终点
func solve(s, t int) int {
	//特判
	if t <= s {
		return s - t
	}

	MAXN := t*2 + 5

	//模拟队列
	q := make([]int, MAXN)
	l, r := 0, 0

	//距离数组
	dis := make([]int, MAXN)

	//初始数据处理
	dis[s] = 1
	q[r] = s
	r++

	for l < r {
		//取出队首
		now := q[l]
		l++
		if now == t {
			break
		}
		//往前走一步
		if now+1 <= MAXN && dis[now+1] == 0 {
			dis[now+1] = dis[now] + 1
			q[r] = now + 1
			r++
		}
		//往后走一步
		if now-1 > 0 && dis[now-1] == 0 {
			dis[now-1] = dis[now] + 1
			q[r] = now - 1
			r++
		}
		//坐公交
		if now*2 <= MAXN && dis[now*2] == 0 {
			dis[now*2] = dis[now] + 1
			q[r] = now * 2
			r++
		}
	}
	return dis[t]-1
}