← 返回题目列表

坏了的计算器为什么要反向贪心?目标数奇偶如何决定操作?

中等 第 26 / 29 题 更新于 2026/08/01
贪心反向思维数学坏了的计算器

简化版

坏了的计算器从 startValue 正向到 target 不好贪心,反向从 target 回到 startValue 更简单。若 target 是偶数,就反向除以 2;若是奇数,就反向加 1 变偶数。直到 target <= startValue,剩下只能用减一的逆操作补齐。

详细版

正向操作是乘 2 或减 1。反向操作就是除以 2 或加 1。目标大于起点时,反向尽量除以 2 能最快缩小规模;但只有偶数能除,奇数必须先加 1 变成偶数。目标不大于起点后,正向只需要执行 startValue - target 次减 1。

时间复杂度 O(log target),空间复杂度 O(1)。这题的核心是反向思维:把难以选择的乘 2 / 减 1,变成更明确的除 2 / 加 1。

完整版教学

一、为什么正向不好贪心

startValue 出发,你每步可以乘 2 或减 1。看起来应该尽量乘 2,但有时乘过头后需要多次减回来;不乘又可能太慢。

start=3, target=10
3 -> 6 -> 12 -> 11 -> 10,共 4 步
3 -> 2 -> 4 -> 8 -> 16 -> ... 更绕

正向时很难判断什么时候该乘,什么时候该减。

二、反向后操作为什么更清晰

把目标倒着变回起点。正向乘 2 的逆操作是除以 2,正向减 1 的逆操作是加 1。

正向操作反向操作
x * 2x / 2
x - 1x + 1

记忆钩子:正向选择纠结时,看看反向是否能让每一步变成唯一最优。

当目标比起点大时,除以 2 的缩小效率远高于加 1,所以能除就除。

三、为什么偶数目标要除以 2

如果反向目标 target 是偶数,它可以由正向某个数乘 2 得到。反向除以 2 会快速接近起点。

target = 20
20 -> 10 -> 5

如果不除而选择加 1,目标会变大,通常只会让路径更长。偶数时除以 2 是最直接消除一次正向乘法的方式。

四、为什么奇数目标要加 1

奇数不能直接除以 2。反向只剩加 1 这条有效路,因为目标仍大于起点时,继续变大一步是为了变成偶数,下一步能除以 2。

target = 11
11 -> 12 -> 6

这对应正向路径里的:

6 -> 12 -> 11

也就是先乘 2 再减 1。

五、什么时候停止反向循环

target <= startValue 时,反向再除以 2 或加 1都没有必要。正向从 startValue 到较小的 target,只能通过减 1 完成。

start=10, target=6
10 -> 9 -> 8 -> 7 -> 6,需要 4 步

所以最后直接加上:

startValue - target

这个尾巴是很多人漏掉的边界。

六、代码模板

int brokenCalc(int startValue, int target) {
    int steps = 0;
    while (target > startValue) {
        if (target % 2 == 0) {
            target /= 2;
        } else {
            target += 1;
        }
        steps++;
    }
    return steps + (startValue - target);
}

循环只在目标仍大于起点时进行反向贪心。每次偶数除半,奇数先加一,目标规模整体会很快下降。

七、常见误区与追问

  • 误区:正向一直乘 2 到超过目标。 超过多少会影响回退成本,不容易直接最优。
  • 误区:反向奇数时减 1。 减 1 不是正向操作的逆操作;正向没有 +1,所以反向没有 -1
  • 误区:target 小于 start 后继续反向处理。 此时正向只能减 1,直接计算差值。
  • 追问:为什么偶数一定除以 2? 除以 2 是最快缩小目标并对应一次正向乘法的操作。
  • 追问:复杂度为什么接近对数? 大部分操作会把目标除以 2,奇数也只需加一次再除。
  • 追问:这类题什么时候考虑反向? 正向操作有“扩大”和“回退”纠结,而逆向操作更确定时。

八、加强记忆

坏了的计算器别从起点硬冲,倒着看更清楚。目标大于起点时,偶数就除以 2,奇数就加 1 变偶数;目标不大于起点后,剩下就是起点减到目标的步数。反向贪心的关键是把“乘不乘”的纠结变成“能不能除”的确定判断。