当青训营遇上码上掘金
主题 3:寻友之旅
题目
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。 步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1 公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
思路
- 如果有错误希望大佬指正
- 这个题目,在我印象里和有一年蓝桥杯的的一个填空题很像
- 思路一:广度优先搜索(BFS)
- 思路二:贪心(GA)
- 两个思路的代码都放在了最后
思路一:广度优先搜索(BFS)
这个题和迷宫很像,只是将东南西北四个方向换成了x+1、x-1、2*x
我们只需要从小青的位置开始一层一层的搜索,把先搜索到的点进栈(如果步数已经有记录则不进栈),记录到达该点花了多少步,如果搜完以某个节点开始的一层,则将该点出栈,然后从栈顶取出一个元素继续搜索,直到小青到达小码的位置为止。
思路二:贪心(GA)
这个题贪心思路的正确性证明我不会,但是应该是对的
先将N的值不断的乘二,找到离K最近的一次,差的绝对值记录为distance,乘二的次数记录为count1
然后将distance分解为最少的(正负)pow(2,n)之和(n>=0),这些数的个数记为count2,最终的结果就是count1+count2。这里我的理解就是如果其中有一个数是4(2的2次幂),那么就需要在倒数第二次乘二之前加1或减1(1在这里表示2的0次幂),这样就使得最终结果相差了4。
例如N=3,K=18,那么distance=6(18-3*2*2),count1=2,然后我们可以将distance分解为2+4,则count2=2,最终的结果为count1+count2=2+2=4。实际上最优的路径为:((3+1)*2+1)*2,将该式变形为:(3*2+2+1)*2=(3*2*2+4+2),这个式子就是我贪心思路中的式子
代码
package main
import (
"fmt"
)
//「青训营 X 码上掘金」主题创作活动主题3
/*
主题 3:寻友之旅
小青要找小码去玩,他们的家在一条直线上,当前小青在地点 N ,小码在地点 K (0≤N , K≤100 000),并且小码在自己家原地不动等待小青。小青有两种交通方式可选:步行和公交。
步行:小青可以在一分钟内从任意节点 X 移动到节点 X-1 或 X+1
公交:小青可以在一分钟内从任意节点 X 移动到节点 2×X (公交不可以向后走)
*/
//思路一:广度优先搜索(BFS),具体的思路可以看我青训营专栏里的文章
func theme3BFS() int {
var N, K = 2, 19 //我这里N和K的值没有从控制台输入,如果需要的话可以自己使用fmt.Scanf
if N >= K { //如果小青没在小码的后面,那么她只有一步步往后步行,当然也可以不做判断,直接BFS
return N - K
}
var count = make([]int, 2*K+1, 2*K+1)
var stack = make([]int, 0, 2*K+1)
stack = append(stack, N)
for len(stack) != 0 {
position := stack[0]
stack = stack[1:]
//这里做了3次差不多的判断,有兴趣的话可以自己抽出一个函数
if count[position+1] == 0 { //判断是否已经搜索过了这个点
count[position+1] = count[position] + 1
if position+1 == K {
break
}
stack = append(stack, position+1)
}
if position*2 < 2*K && count[position*2] == 0 { //另外做了判断,并且判断的顺序不能改变(利用短路与的特性)
count[position*2] = count[position] + 1
if position*2 == K {
break
}
stack = append(stack, position*2)
}
if position-1 > 0 && count[position-1] == 0 { //另外做了判断,同上
count[position-1] = count[position] + 1
if position-1 == K {
break
}
stack = append(stack, position-1)
}
// fmt.Println(stack)
}
return count[K]
}
//思路二:贪心(GA),具体的思路可以看我青训营专栏里的文章
func theme3GA() int {
var N, K = 2, 19 //我这里N和K的值没有从控制台输入,如果需要的话可以自己使用fmt.Scanf
if N >= K { //如果小青没在小码的后面,那么她只有一步步往后步行,当然也可以不做判断,直接贪心
return N - K
}
var count, distance, position int = 0, K - N, N //如果上面不判断,那么distance=abs(K-N)
for {
if distance <= abs(position*2-K) {
break
}
count++
distance = abs(position*2 - K)
position *= 2
}
// fmt.Printf("distance: %v\n", distance)
t := 0
for distance != 0 {
//找到不大于distance的 最大的2的n次幂
n := 0
for {
if pow(2, n+1) > distance {
break
}
n++
}
distance = distance - pow(2, n)
t++
// fmt.Printf("n: %v\n", n)
}
return count + t
}
func abs(key int) int { //求绝对值
if key < 0 {
return key * -1
}
return key
}
func pow(x int, n int) int { //快速幂
ans := 1
x_contribute := x
for n > 0 {
if n%2 == 1 {
ans *= x_contribute
}
x_contribute *= x_contribute
n /= 2
}
return ans
}
func main() {
// fmt.Println(theme3BFS())
fmt.Println(theme3GA())
}