362 坏计算器的最小操作问题 | 豆包MarsCode AI刷题

165 阅读4分钟

问题描述

小M有一个坏掉的计算器,它有两种功能可以操作屏幕上显示的数字:

  1. 将显示的数字乘以2;
  2. 将显示的数字减去1。

现在,计算器上显示的数字是 x,小M希望通过最少的操作次数,将数字变为 y

请你帮忙计算一下,最少需要多少次操作才能将数字从 x 变为 y


测试样例

样例1:

输入:x = 2,y = 3
输出:2

解析:2→4→3,共 2 次操作。

样例2:

输入:x = 4,y = 7
输出:2

解析:4→8→7,共 2 次操作。

样例3:

输入:x = 3,y = 66
输出:9

解析: 3→6→5→10→9→18→17→34→33→66,共 9 次操作。

解题思路

这个问题可以通过动态规划或贪心算法解决,但需要注意不同的优化思路。


方法 1:动态规划

思路

我们使用一个数组 dp[i] 表示将数字 i 转化为目标数字 y 所需的最小操作次数。通过递推公式进行求解。

  1. 当 i≤x直接通过减法操作从 x减到 y,操作次数为 dp[i]=x−i。

  2. 当 i>x我们需要考虑如何通过之前的状态递推:

    • 如果选择将某个数字 j 乘以 2 达到 i,那么所需的操作次数为 dp[j]+1;
    • 如果需要通过减法从 i 到某个较小的数字 k,我们可以递归地考虑 dp[k]。

动态规划代码实现

def solution(x: int, y: int) -> int:
    if y <= x:
        # 如果目标值小于等于初始值,只能通过减法操作
        return x - y
    else:
        # 动态规划数组
        dp = [0 for _ in range(y + 1)]

        # 遍历所有可能的值
        for i in range(y + 1):
            if i <= x:
                # 当当前值小于等于 x,直接用减法到达目标
                dp[i] = x - i
            else:
                # 当前值大于 x,需要通过状态转移找到最小操作次数
                temp = []
                for j in range(1, i):
                    num = j
                    t = dp[j]  # 从 dp[j] 开始
                    while num < i:
                        num *= 2
                        t += 1  # 乘法次数
                    t += (num - i)  # 剩余通过减法操作
                    temp.append(t)
                dp[i] = min(temp)
        
        return dp[y]

# 测试样例
print(solution(2, 3))  # 输出: 2
print(solution(4, 7))  # 输出: 2
print(solution(3, 66))  # 输出: 9

时间复杂度

该算法需要计算所有 dp[i]的值,因此时间复杂度为 O(y2),对于较大的 y可能较慢。

方法 2:贪心算法(优化解法)

动态规划的计算复杂度较高,当 y很大时,可能无法快速计算。此时可以采用贪心算法,从目标数字 y开始反推到 x。

核心思路

  1. 如果 y 是偶数,我们优先通过除法操作缩小到 y/2;
  2. 如果 y 是奇数,我们先通过加法使其变为偶数;
  3. 当 y≤x 时,直接通过加法将 y 增加到 x。

贪心算法实现

def solution(x: int, y: int) -> int:
    operations = 0
    while y > x:
        if y % 2 == 0:
            y //= 2  # 如果 y 是偶数,优先进行除法操作
        else:
            y += 1  # 如果 y 是奇数,先加 1 使其变为偶数
        operations += 1
    # 当 y <= x 时,只需进行加法操作
    return operations + (x - y)

# 测试样例
print(solution(2, 3))  # 输出: 2
print(solution(4, 7))  # 输出: 2
print(solution(3, 66))  # 输出: 9

贪心算法解析

  1. 从目标数字 y开始反推,确保每一步操作都尽可能减少目标值;
  2. 当目标值变得小于或等于初始值 x时,直接通过加法补齐差值;
  3. 整体复杂度为 O(log⁡y),适合处理较大的输入规模。

复杂度分析

方法时间复杂度空间复杂度
动态规划O(y2)O(y)
贪心算法O(log⁡y)O(1)

总结

在这道题中,我们使用了动态规划和贪心算法两种方法。动态规划适合处理小规模问题,而贪心算法则在较大规模下表现出色。最终,贪心算法是更优的解法,其核心在于从目标数字 y开始反推,确保每一步操作都能最大化减少目标值。