寻友之旅GO语言BFS剪枝思路分享

89 阅读2分钟

当青训营遇上码上掘金

首先把题目贴出来:

主题 3:寻友之旅

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

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

  • 错误的贪心

由于我的审题错误,一开始以为N始终等于0,将这道题看成了一道贪心题,直到上手做时才发现不对。
在N与K均不固定的情况下,贪心算法不能确定哪种是最优解。
如:N=5,K=8
最优解为:5->4->8,但贪心算法并不能搜出这样的解。

  • 正确的BFS

这是一道典型的bfs题,当小青出在N点时,在一分钟内他有三种选择:

  1. 步行至N+1
  2. 步行至N-1
  3. 乘公交至2xN

因此在每个节点对这三种可能进行广度优先搜索,即可得到正确答案。

  • 剪个枝

但这种方式在性能上有一定的欠缺。
如:N=300,K=400,如果小青坐公交,那么直接就到了600处,这样的话,小青距离小码有200,而上一步小青距离小码只有100,关键是200的距离还不能坐公交(只能走着回去),所以这肯定不是最优解。
因此,我在代码中增添了一步剪枝操作:

if tmp3.pos >= 0 && tmp3.pos <= 100000 && abs(tmp3.pos-k) < abs(curNode.pos-k) {
	que.Enqueue(tmp3)
}

注意abs(tmp3.pos-k) < abs(curNode.pos-k)即为剪枝的具体操作。

  • 注意下特殊情况

最后我们需要注意N>K的特殊情况: 如:N=400,K=200,若用BFS需要2^200的搜索空间,对于这样的小问题而言开销十分恐怖。
因此我们需要在前面加上特殊判断:

if n > k {
    fmt.Println(n - k)
    return
}

这样就大功告成了!

  • 测试输入输出
  1. N=345,K=678

image.png

  1. N=678, K=345

image.png

  1. N=345, K=6789

直接卡死

经测试,纵使已经剪过枝,该方法也仅限于搜索层数较少时可用。
(正解应该是DP)