最小步数求解思路
问题描述
从位置 x
到位置 y
的最小步数问题,每一步的长度是正整数,每步的值为上一步的值 -1、0 或 +1,第一步和最后一步必须是 1。
样例分析
- 输入
(12, 6)
,最小步数为 4。 - 输入
(34, 45)
,最小步数为 6。 - 输入
(50, 30)
,最小步数为 8。
解法一:归纳总结+二叉搜索
-
这里看这个示例突然想到了,1.2.3.4……n这个排列的和,然后就发现 (n*(n-1)/2)*2 = 差值,最后return n✖️2
-
有了这个思路之后再看看示例,6,11,20,而 n*(n-1)/2 公式中 n 大致为2,3,4,所以步数是4,6,8
但是假如 差值14呢? 最小步数是
1
,2
,3
,2
,3
,2
,1
是7步,但是最中间的‘2’的值可以是,2,3,4,所以7步实现的差值范围是,[14,16]
-
这里其实有点启发,那思路其实完全可以是,走n最大实现的差值是多少?然后去二叉搜索这个n呀
// 这里归纳一下:
// 1步 : 最多走1个距离
// 2步 : 最多走 2个距离 -> (2*4)/4
// 3步 : 最多走4个距离 ->((3-1)*(3-1+2))/2+(3+1)/2
// 4步 : 最多走6个距离 ->(4*6)/4
// 5步 :最多走 9个距离
// 6步 :最走12个距离 ->(6*8)/4
// 7步 :最多走 16个距离
// 8步 : 最多走 20个距离 -> (8*10)/4
所以:通过归纳法总结出公式了,
当n = 偶数 ,最大可以走 *(n(n+2))/4**个距离,当
n = 奇数,n=n-1,最大可以走 ((n-1)*(n+1))/4 + (n+1)/2步
既然公式有了,就可以直接用二叉搜索,进行逼近
解法二:公式推导
-
步长变化规律:
- 由于每一步的步长只能变化
-1
、0
或+1
,并且步长必须为正整数,这意味着步长序列是一个先增加后减少的序列,或者保持不变,然后减少,形成一个“山峰”形状。 - 第一步和最后一步的步长为
1
,进一步强化了步长序列的对称性。
- 由于每一步的步长只能变化
-
考虑步长序列的可能形式:
-
为了使总步数最少,步长序列应该尽可能地先增加步长,然后再减少步长。
-
例如,对于步长最大值为
m
的序列,步长可以是:- 当步数为奇数时:
[1, 2, 3, ..., m, ..., 3, 2, 1]
,步数为2m - 1
。 - 当步数为偶数时:
[1, 2, 3, ..., m, m, ..., 3, 2, 1]
,步数为2m
。
- 当步数为奇数时:
-
-
建立步数与距离的关系: (这里的公式推导其实是等差数列)
-
当步数为奇数(
N = 2m - 1
)时:- 总距离
S = 1 + 2 + 3 + ... + m + ... + 3 + 2 + 1 = m^2
。
- 总距离
-
当步数为偶数(
N = 2m
)时:- 总距离
S = 1 + 2 + 3 + ... + m + m + ... + 3 + 2 + 1 = m(m + 1)
。
- 总距离
-
-
推导最小步数的计算方法:
-
给定距离
D = |y - x|
,需要找到最小的m
,使得总距离S
不小于D
。 -
算法步骤:
-
初始化
m = 1
。 -
判断:
- 如果
D <= m^2
,则最小步数为N = 2m - 1
。 - 如果
m^2 < D <= m(m + 1)
,则最小步数为N = 2m
。 - 否则,
m += 1
,重复上述判断。 - (这里奇数和偶数的m时候,是总步长的最大值)
- 如果
-
-
public static int solution(int xPosition, int yPosition) {
int D = Math.abs(xPosition - yPosition);
if (D == 0) return 0;
int m = 0;
while (true) {
m++;
if (D <= m * m) {
return 2 * m - 1;
} else if (D <= m * (m + 1)) {
return 2 * m;
}
}
}
解法三:动态规划(失败了)
这个题不适合用dp,记录一下思考过程,有同学发现问题的话麻烦给一些建议
动态规划
这里求最小,然后又给出了“每步的值等于上一步的值 `-1`, `+0`,`+1`”
这样的选择,很自然联想到了动态规划
动态规划四个要素 1.状态 2.选择 3.定义dp 4.转移方程
1. 状态有哪些?步长 ,第i步的值,第i步最大能到达的值
选择是什么?每步的值等于上一步的值 `-1`, `+0`,`+1`
基于这些条件我们可以怎么定义p数组?
dp[i][j] 表示 第i步的最大的步长和是j
但是这样用不到选择呀
dp[i][j] 表示 第i步的步长是j,然后总和是最大
dp[i][j] = dp[i-1][j]或dp[i-1][j]或dp[i-1][j]
但是这样的条件有什么用呢?
什么时候选什么条件呢?
if (remain>n/2) ,这个时候就放心加 dp[i][j] = dp[i-1][j]+1
if(remain==n/2) dp[i][j] = dp[i-1][j]
if(remain<n/2) dp[i][j] = dp[i-1][j]-1
if(remain==0) return i+1;
但是这里问题变得复杂了又要解决奇偶问题了
所以这里dp[i][j]表示到达第i步到达最小位置j所需要的最小步数