所以我很担心这个递归函数是如何工作的。我不明白这究竟是如何产生答案的。问题陈述:
编写一个返回最小步数的递归函数 将X转换为Y所必需的。如果X> Y,返回1000000000,表示不存在解决方案。(例如,如果X = 13且Y = 28,则正确的响应为2 - 首先,您将添加1到13以获得14,然后将14乘以2以获得28 。)随意调用提供的函数
这是解决方案:
int min(int x, int y) {
if (x < y) return x;
return y;
}
// Returns the minimum number of steps to transform x into y, or
// 100000000 to indicate no solution.
int minSteps(int x, int y) {
if (x > y) return NO_SOLUTION;
if (x == y) return 0;
int mult = 1 + minSteps(2*x, y);
int add = 1 + minSteps(x+1, y);
return min(add, mult);
}
如果有人可以请解释那个很棒的解决方案。谢谢!
答案 0 :(得分:2)
可以通过递归解决的许多问题的核心在于将原始问题减少到较小问题并继续该过程直到它减少到已知问题的原则。
这个问题完全适合这种方法。
您的答案是一系列将x
转换为y
的算术运算。即,像这样:
x?一个 ? b? C ? ......? ÿ
其中?
表示乘以2
或添加1
;和a
,b
,c
...表示对先前结果应用操作后的中间结果。例如,可以用这种方式描述5
到22
的转换:
5(* 2)10(+1)11(* 2)22
现在让我们回到减少原则。从给定的x开始,我们需要选择第一步。它可以是*2
OR [1] +1
,我们还不知道,所以我们需要检查它们。在*2
的情况下,x
转换为2x
,如果是+1
,则x
转换为x+1
。瞧,我们迈出了一步,减少了问题!现在我们要解决两个较小的问题 - 一个用于2x
,另一个用于x+1
,并找到结果之间的最小值。由于我们计算了这些步骤,因此我们创建了2
个不同的计数器(每个操作类型一个),并为每个计数器添加1
(因为我们已经执行了一个步骤)。为了完成每个计数器的实际值的计算,我们需要解决两个较小的问题 - 为了解决它们,我们用新输入递归调用函数(两次,每次输入一次)。算法以这种方式继续,每次都会减少问题,直到达到停止条件,这可能是x == y
(它是有效转换)或x > y
(无效转换)。在x == y
的情况下,需要完全0
个步骤并且执行停止,导致调用堆栈回退,填充发起递归分支的计数器的实际值。在x > y
的情况下,结果是1000000000
(假设它太大而不是实际结果,因此总和将被丢弃,大于第二个分支的总和)。通过使用递归树进行可视化,通常可以更好地理解这个过程(例如,参见@DavidBowling回答。错误,由于某种原因被删除了......)。
[1] 虽然在这个问题上很清楚,但有时候操作之间的区别可能很模糊。将问题分解为许多较小的问题非常重要,在它们之间没有任何重叠。
答案 1 :(得分:1)
这实际上是帮助教授递归的一个很好的例子。不确定我是否可以解释它,但会拍摄。需要说明的是,只有两种步骤:将X加倍,或者将X加1。
理解这一点的最佳方法是通过代码示例。
我暂时删除了其余的答案。在调试器中使用它。它实际上非常优雅,但我还没有觉得我可以在再玩它之前解释它的工作原理。
仍然没有解释它,但看看这个:
minSteps(16, 28) = 12
minSteps(15, 28) = 13
minSteps(14, 28) = 1
minSteps(13, 28) = 2
minSteps(12, 28) = 3
minSteps(11, 28) = 4
minSteps(10, 28) = 5
minSteps( 9, 28) = 6
minSteps( 8, 28) = 7
minSteps( 7, 28) = 2
minSteps( 6, 28) = 3
minSteps( 5, 28) = 4
minSteps( 4, 28) = 5
minSteps( 3, 28) = 4
minSteps( 2, 28) = 5
minSteps( 1, 28) = 6
特别注意:
minSteps( 5, 28) = 4 // x+1 twice (5->7), then x*2 twice (7->14->28)
minSteps( 4, 28) = 5 // x+1 thrice (4->7), then x*2 twice (7->14->28)
minSteps( 3, 28) = 4 // x*2 (3->6), then x+1 (6->7), then x*2 twice (7->14->28)
minSteps( 2, 28) = 5 // x+1 (2->3), then x*2 (3->6), then x+1 (6->7), then x*2 twice (7->14->28)
对我来说,似乎相对容易看出算法如何能够在任何情况下重复乘以2,或者任何情况下首先加1次,然后重复乘以2。这几乎是上述每种情况下的最小步数。
但minSteps(3,28)和minSteps(2,28)的情况确实非常有趣,因为这些情况的最小步数涉及在x * 2和x + 1之间来回切换。然而算法正确。
这些案件实际上没什么特别之处。答案是这样的:过程总是二进制的。在每个步骤中,该问题被分解为该步骤的x * 2和x + 1,以及每个下一步的问题,如下所示:关键是,通过这种方式,算法实际上测试了每个可能的路径(每个x + 1s和x * 2s的可能组合 并占用所有可能路径的最小值。起初我并不明白它是在尝试每条路径。当然,一旦超过Y,它就会放弃任何超过Y的路径。