计算从整数位置 x 到 y 的最少步数(Golang)

174 阅读3分钟

问题描述

AB 实验同学每天都很苦恼如何可以更好地进行 AB 实验,每一步的流程很重要,我们目标为了缩短所需的步数。 我们假设每一步对应到每一个位置。从一个整数位置 x 走到另外一个整数位置 y,每一步的长度是正整数,每步的值等于上一步的值 -1+0+1。求 xy 最少走几步。并且第一步必须是 1,最后一步必须是 1,从 xy 最少需要多少步。

思路

假设最大步长为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的距离我们一步就可以走完(将它插入到我们已经计算的连续等差数列中去)例如题目中所给样例答案,123221,利用上面的思路,其中最大的步长为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)
}

参考资料:计算位置X到Y的最少步数-算法题解-CSDN博客