这是我参与「第五届青训营 」笔记创作活动的第七天,今天主要完成码上掘金活动的内容~
题意
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 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
}