https://leetcode-cn.com/problems/broken-calculator/comments/
在显示着数字的坏计算器上,我们可以执行以下两种操作:
- 双倍(Double):将显示屏上的数字乘 2;
- 递减(Decrement):将显示屏上的数字减 1 。
最初,计算器显示数字 X
。
返回显示数字 Y
所需的最小操作数。
示例 1:
输入:X = 2, Y = 3 输出:2 解释:先进行双倍运算,然后再进行递减运算 {2 -> 4 -> 3}.
示例 2:
输入:X = 5, Y = 8 输出:2 解释:先递减,再双倍 {5 -> 4 -> 8}.
示例 3:
输入:X = 3, Y = 10 输出:3 解释:先双倍,然后递减,再双倍 {3 -> 6 -> 5 -> 10}.
示例 4:
输入:X = 1024, Y = 1 输出:1023 解释:执行递减运算 1023 次
提示:
1 <= X <= 10^9
1 <= Y <= 10^9
首先这一题的思路是
先逆向思维,把题目倒过来,理解为X不变,Y只能进行除2或者+1运算,要使得最后两者结果相等。
为什么要进行颠倒呢,因为倒过来会发生的情况比较少,如果是X进行*2或者-1运算运算,那么在X为任何值的时候都可以进行两种操作,而倒过来计算,Y进行除2或者+1运算,那么当Y为奇数的时候,就不能进行除2运算。这样就减少了一些可能性。
如果把题目倒过来后,总共有三种情况
第一种情况,如果Y<X,那么只能通过Y递增运算来接近了,这种情况下,如果使用除2 运算,无论在哪一步进行除2都会使得Y越来越远离X
第二种情况,是X<Y,并且Y是奇数,如果Y是奇数,那么就不能进行除2运算了,因为会无法整除,因此在这里如果Y为奇数,那么只能进行递增运算,把Y变成偶数(然后下一步才可以进行除2运算)
第三种情况,也就是X<Y,并且Y是偶数,这个情况下,即可用递增运算,也可以用除2运算,在这种时候采用除2运算优于+1运算,因此这种情况直接采用除2运算,至于为什么会这样?
这里来推论一下,首先因为Y是偶数,因此如果进行递增运算,后无法进行除2运算,这种情况下,也就是递增运算最少需要用两次(因为X<Y因此还需要进行除2运算来逼近X,因此除2运算是必须使用的)然后采用除2运算逼近X, 从上述得知,除2运算是必须的
区别就是,是否要进行两次递增运算,然后进行除2运算,还是直接除2运算, 从数学运算可以得知(Y+1+1)/2=Y/2+1,需要三次运算,而Y/2+1只需要两次运算,从而得知,在第三种情况下,进行直接除2是永远比先递增然后除2要完美的
最后说一下,为什么不直接正向求解,原因就是这样,如果直接求解X到Y
那么首先由的情况的第一种X>Y,这个只能进行递增
第二个情况,X<Y,然后这种时候需要计算的情况就多了,首先如果Y是奇数,那么就可以得知最后一次运算不可能是X*2得到的,只可能是X-1,然后就可以吧题目换算成X到(Y-1),这个时候就进入第三种情况了
第三个情况,X<Y,并且Y是偶数,这种时候,X要最少次数达到Y的状态,那么还是一样的选择,因为Y是偶数,所以最后一次是*2运算,因此计算是(X-1)*2或者X*2【至于为什么不是(X-1-1)*2这些可以递归得到,因此不多列举】,首先也对结果进行求值,那么结果是2X-2【两次操作】和2X【一次操作】
那么似乎有可能结果刚刚好是2X-2的时候,X先-1然后*2优于2X ?实际上,这种时候就需要看Y了,看看哪种方法能更快逼近X
最后发现,还是要看Y,那么为什么不直接从Y开始逆推?
最后上代码
python解法
class Solution(object):
# 思路,主要有几种情况,第一,Y小于X,那么这种情况下,只能进行疯狂递减操作,需要进行X-Y次操作
# 第二,Y>X,那么首先观察,Y是否为奇数,如果是奇数,那么只能Y先+1,如果是偶数,那么就可以直接Y除2,查看是够相近
def brokenCalc(self, X, Y):
"""
:type X: int
:type Y: int
:rtype: int
"""
# 递归思路
# if X == Y:
# return 0
# if X > Y:
# return X - Y
# if Y % 2 == 0:
# return 1 + self.brokenCalc(X,int(Y/2))
# else:
# return 1 + self.brokenCalc(X,Y+1)
# 循环思路
allnum = 0
while X != Y:
if X > Y:
allnum += (X - Y)
break
if Y % 2 == 0:
Y /= 2
allnum+=1
else:
Y += 1
allnum+=1
return int(allnum)