C++ 中通过加减从 1 开始的数字使 X = Y 的最小移动次数

2025年2月11日 | 阅读 9 分钟

在数学问题解决领域,没有什么挑战比将一个数字通过一系列加减运算转化为另一个数字更能吸引人。这个任务通常被概括为寻找使两个数字相等的最小移动次数的问题,由于其结合了算术技巧和算法的巧妙,因此具有特殊的吸引力。在本文中,我们将踏上探索该问题复杂性的旅程,并使用 C++ 编程的力量设计出优雅的解决方案。

理解问题

问题的核心是一个看似简单的前提:给定两个数字 X 和 Y,我们的任务是从 1 开始,通过一系列加减运算将 X 转化为 Y。每个操作涉及将当前数字加上或减去 1。目标是确定达到 X 和 Y 之间期望的相等性所需的最小此类移动次数。

问题的核心是一个看似简单但又引人入胜的挑战:通过一系列加减运算(从 1 开始)将一个数字转化为另一个数字。这个问题包含了算术操作和算法优化的精髓,吸引我们深入探讨其复杂性并揭示其基本原理。

为了充分理解这个问题,让我们将其分解为组成部分,并详细探讨每个方面。

  1. 目标:问题的首要目标是确定将给定数字 X 转化为另一个数字 Y 所需的最小移动次数。这些移动包括对当前数字加 1 或减 1,目标是最终达到目标数字 Y。
  2. 操作:此问题中允许的操作很简单:加 1 或减 1。这些操作是转换过程的构建块,通过渐进式调整引导我们走向期望的目的地。
  3. 起点:我们转换旅程的起点是数字 1。从这个卑微的开端开始,我们踏上穿越数字景观的旅程,穿越加减法的复杂路径以达到我们的目的地。
  4. 数字 X 和 Y:问题围绕两个关键数字:X 和 Y。X 代表我们要转换的初始数字,而 Y 代表我们要达到的目标数字。这些数字是我们探索的锚点,引导我们在数字领域中的轨迹。
  5. 最小移动次数:问题的关键在于确定将 X 转换为 Y 所需的最小移动次数。此指标作为效率的衡量标准,引导我们的算法工作朝着优化解决方案,以最大限度地减少计算开销并最大限度地提高性能。
  6. 复杂性:尽管表面上很简单,但这个问题隐藏着与其表面优雅相悖的复杂性。算术运算、数字操作和算法优化的相互作用赋予了这个问题丰富的复杂性,吸引我们以好奇和勤奋的态度探索其深度。

考虑一个例子来说明这个问题:设 X = 5,Y = 9。要将 5 转换为 9,我们可以执行以下一系列操作

将 5 加 1 得到 6。

将 6 加 1 得到 7。

将 7 加 1 得到 8。

将 8 加 1 得到 9。

在这种情况下,将 X 转换为 Y 需要四次移动。然而,可能存在其他操作序列,它们可以产生相同的结果,甚至更少的移动次数。我们的目标是设计一种算法,该算法可以有效地确定任何给定数字对 X 和 Y 所需的最小移动次数。

解决问题的方法

为了在 C++ 中解决这个问题,我们可以采用动态规划方法,该方法系统地探索所有可能的操作序列以找到最佳解决方案。动态规划的本质在于将复杂问题分解为更小的子问题,只计算每个子问题一次,并将子问题的解决方案存储在表中以供将来参考。这种方法使我们能够避免冗余计算并有效地找到最佳解决方案。

让我们概述我们的动态规划算法的步骤

初始化:初始化一个表来存储将每个数字从 1 转换为 Y 所需的最小移动次数。最初,将表中的所有条目设置为一个很大的值,表示它们尚未计算。

基本情况:将与 1 和 Y 对应的表条目的值设置为 0,因为将 1 转换为自身或 Y 转换为自身不需要移动。

迭代计算:迭代从 2 到 Y 的每个数字,并计算将它转换为 Y 所需的最小移动次数。对于每个数字 i,考虑两个选项

  • 将 1 加到 i 并检查到达 Y 所需的移动次数。
  • 从 i 中减去 1 并检查到达 Y 所需的移动次数。
  • 最佳解决方案:一旦填充了表,将 X 转换为 Y 所需的最小移动次数就存储在与 X 对应的条目中。

输出:输出将 X 转换为 Y 所需的最小移动次数。

本文概述的动态规划算法是一种系统的方法,可有效地找到通过加减数字(从 1 开始)将一个数字转换为另一个数字所需的最小移动次数。让我们详细研究此算法的步骤

  1. 初始化:我们的动态规划算法的第一步是初始化一个表来存储将每个数字从 1 转换为目标数字 Y 所需的最小移动次数。我们创建一个大小为 Y+1 的向量或数组 dp 来容纳从 1 到 Y 的所有数字。我们将此表中的所有条目初始化为较大的值(例如 INT_MAX),以表示它们尚未计算。
  2. 基本情况:我们将与数字 1 和 Y 对应的表条目的值设置为 0,因为将 1 转换为自身或 Y 转换为自身不需要移动。这些作为我们动态规划递归的基本情况。
  3. 迭代计算:我们迭代从 2 到 Y 的每个数字,填充表 dp 的条目。对于每个数字 i,我们考虑两个选项
    • 将 1 加到 i:我们检查将 1 加到 i (i + 1) 是否在 Y 的范围内。如果是,我们将 dp 中的相应条目更新为当前值与 dp[i + 1] 中存储的值(加 1)之间的最小值。
    • 从 i 中减去 1:同样,我们检查将 1 从 i (i - 1) 中减去是否在大于 0 的范围内。如果是,我们将 dp 中的相应条目更新为当前值与 dp[i - 1] 中存储的值(加 1)之间的最小值。
  4. 最佳解决方案:一旦我们填充了表 dp,将 X 转换为 Y 所需的最小移动次数就存储在与 X 对应的条目中。此值代表我们问题的最佳解决方案。
  5. 输出:最后,我们输出将 X 转换为 Y 所需的最小移动次数,该次数从表 dp 中检索。

通过遵循这些步骤,我们的动态规划算法系统地计算了将一个数字转换为另一个数字所需的最小移动次数,利用最优子结构和重叠子问题的原理来避免冗余计算并实现效率。

这种方法使我们能够以精确和优雅的方式处理问题,提供一个鲁棒的解决方案,能够高效地处理各种输入大小。通过迭代计算、最优子结构和动态规划原理的相互作用,我们揭示了问题的复杂性,并提出了一个在计算复杂性和算法效率之间取得平衡的解决方案。

C++ 中的实现

现在,让我们将概述的算法转化为 C++ 代码

输出

 
Enter the values of X and Y: 3 7
Minimum moves required: 4

说明

  • 它初始化一个大小为 Y + 1 的向量 dp,并将每个元素初始化为 INT_MAX。
  • 它将 dp[1] 设置为 0,作为基本情况。
  • 它从 i = 2 迭代到 Y,并考虑两个选项
  • 选项 1:将 1 加到 i,并用 dp[i] + 1 和 dp[i + 1] 的当前值之间的最小值更新 dp[i + 1]。
  • 选项 2:从 i 中减去 1,并用 dp[i] + 1 和 dp[i - 1] 的当前值之间的最小值更新 dp[i - 1]。
  • minMoves 函数返回 dp[X],它表示最小移动次数。

复杂度分析

时间复杂度分析

  • 初始化:初始化步骤涉及创建一个大小为 Y+1 的表并初始化其条目。此步骤的时间复杂度为 O(Y),因为它需要迭代表中的 Y+1 个元素。
  • 基本情况:将数字 1 和 Y 的基本情况设置为 0 涉及恒定的时间操作,对总时间复杂度贡献的开销可以忽略不计。
  • 迭代计算:算法的核心在于迭代计算步骤,我们在其中填写从 2 到 Y 的数字的表 dp 条目。对于每个数字 i,我们考虑两个选项:将 1 加到 i 和从 i 中减去 1。此步骤需要迭代从 2 到 Y 的每个数字,并在每次迭代中执行恒定的时间操作。因此,此步骤的时间复杂度为 O(Y)。
  • 最佳解决方案和输出:从表 dp 中检索最佳解决方案并输出它涉及恒定的空间操作,对总时间复杂度贡献的开销可以忽略不计。

考虑到这些因素,动态规划算法的总体时间复杂度主要由迭代计算步骤决定,即 O(Y)。这意味着算法所需的时间随 Y 的值线性增加。

空间复杂度分析

  • 初始化:初始化步骤涉及创建一个大小为 Y+1 的表 dp,用于存储从 1 到 Y 的每个数字的最小移动次数。因此,此步骤的空间复杂度为 O(Y)。
  • 基本情况:设置数字 1 和 Y 的基本情况只需要极少的额外空间,因为我们只在表 dp 中存储恒定值。
  • 迭代计算:在迭代计算步骤中,我们根据所需的最小移动次数更新从 2 到 Y 的数字的表 dp 条目。由于我们只需要存储每个数字的最小移动次数,因此此步骤的空间复杂度为每个迭代 O(1)。因此,此步骤的总空间复杂度仍然是 O(Y)。
  • 最佳解决方案和输出:检索最佳解决方案并输出它涉及恒定的空间操作,对总空间复杂度贡献的开销可以忽略不计。

考虑到这些因素,动态规划算法的总体空间复杂度主要由表 dp 所需的空间决定,即 O(Y)。这意味着算法占用的空间随 Y 的值线性增加。

总之,动态规划算法为查找将一个数字转换为另一个数字所需的最小移动次数的问题提供了一种高效且可扩展的解决方案。该算法的时间复杂度为 O(Y),空间复杂度为 O(Y),表现出出色的性能特征,使其能够高效地处理各种输入大小。


下一个主题C++ 中的凸包算法