问题描述
AB 实验同学每天都很苦恼如何可以更好地进行 AB 实验,每一步的流程很重要,我们目标为了缩短所需的步数。
我们假设每一步对应到每一个位置。从一个整数位置 x 走到另外一个整数位置 y,每一步的长度是正整数,每步的值等于上一步的值 -1, +0,+1。求 x 到 y 最少走几步。并且第一步必须是 1,最后一步必须是 1,从 x 到 y 最少需要多少步。
思路
假设最大步长为k,那么所走步数一定是先从1加到K,然后又从K减为1,因为在答案中可能会有两步步长一样长的情况,所以这个序列真实情况不一定是对称的。
为了方便计算我们现在只表示答案序列中的连续部分,假设答案中的所有数字和为diff(即起点终点之间的距离)前面上升序列 (1+2+......+k)=k*(k+1)/2后面的下降序列(k-1+.......+1)=k*(k-1)/2都是公差为1的等差数列,那么答案序列中未被计算的长度 remain=diff - k*(k+1)/2 - k*(k-1)/2目前已知步数就是 k(前半部分被记入的步数) 和 k-1(后半部分被记入的步数)。
接下来贪心的把剩余步数remain走完,我们已经有了一个连续的步数序列,并且相邻的步数的长度可以相同,那么剩余部分中大于最大步长k的部分我们就用k去走,循环减去k去走就可以了,然后剩余的小于k的距离我们一步就可以走完(将它插入到我们已经计算的连续等差数列中去)例如题目中所给样例答案,1,2,3,2,2,1,利用上面的思路,其中最大的步长为3,我们摘出来上升数列和下降数列后,剩余距离为2,小于最大步长,所以可以直接塞入答案序列当中。
一步就能完成那么这个最大步长k是多少呢?最简单的方式直接从1开始枚举,最大不可能超过两点的距离。如果想要代码节省一点时间,我们可以想一下最大的步长会是多少呢?通过上面我们知道答案序列可以表示为k*(k+1)/2 + k*(k-1)/2+剩余距离,当剩余距离为0,左边的k才可能达到最大值,即k*(k+1)/2 + k*(k-1)/2=diff,化简可以得到k*k=diff,即步长最大为根号diff,所以我们从1枚举到根号diff即可。
代码实现
package main
import (
"fmt"
"math"
)
func solution(x, y int) int {
// Please write your code here
diff := y - x
if diff < 0 {
diff *= -1
}
ans := math.MaxInt
// 枚举步长的最大值,
for i := 1; i*i <= diff; i++ {
sum := maxPoint(i) + maxPoint(i-1) //计算既定的走过的长度
remain := diff - sum //剩余多少
cnt := 0 //剩余距离需要多少步
//按此种最大步长行走 否则退出
if remain < 0 {
break
}
//如果剩余步数大于最大步长,则以最大步长行走
for remain > i {
remain -= i
cnt++
}
//剩余步数大于0小于最大步长,可以一步走出
if remain > 0 {
cnt++
}
ans = min(ans, cnt+2*i-1)
}
return ans
}
func maxPoint(x int) int {
top := x * (x + 1) / 2
return top
}
func min(x, y int) int {
if x > y {
return y
} else {
return x
}
}
func main() {
// You can add more test cases here
// fmt.Println(solution(12, 6))
fmt.Println(solution(12, 6) == 4)
fmt.Println(solution(34, 45) == 6)
fmt.Println(solution(50, 30) == 8)
}