每日一水(AI刷题: 兔群繁殖之谜) | 豆包MarsCode AI刷题

173 阅读4分钟

每日一水

问题描述

生物学家小 R 正在研究一种特殊的兔子品种的繁殖模式。这种兔子的繁殖遵循以下规律:

  1. 每对成年兔子每个月会生育一对新的小兔子(一雌一雄)。
  2. 新生的小兔子需要一个月成长,到第二个月才能开始繁殖。
  3. 兔子永远不会死亡。

小 R 从一对新生的小兔子开始观察。他想知道在第 A 个月末,总共会有多少对兔子。

请你帮助小 R 编写一个程序,计算在给定的月份 A 时,兔子群体的总对数。

注意

  • 初始时有 1 对新生小兔子。
  • 第 1 个月末有 1 对兔子:原来那对变成了成年兔子,并开始繁殖。
  • 第 2 个月末有 2 对兔子:原来那 1 对成年兔子,繁殖了 1 对新生的小兔子。
  • 从第 3 个月开始,兔子群体会按照上述规律增长。

解答

刚刚入门的同学在学习完递归去做练习一定看到过类似问题, 这题用递归方法很容易解出来. 实现思路基本在"注意"栏已经很明晰了, 初始为1对兔子,第二个月末为2对兔子(初始幼兔长大又生了一对), 后面以此类推. 不难推出第n月的兔子数量是前面两个月的兔子数量之和.

  • 不太理解的同学可以参考以下草图:(Y为成兔,y为幼兔)
  • 第一月: y
  • 第二月: Y y
  • 第三月: Y Y y
  • 第四月: Y Y Y y y
  • 第五月: Y Y Y Y Y y y y
  • 第六月: Y Y Y Y Y Y Y Y y y y y y
  • ...
  • 这样就更直观明了了, n-1月的兔子都还活着, 只有n-2月的兔子才能繁殖幼兔. 代码如下
def solution(A: int) -> int:
    # Edit your code here
    if A == 1:
        return 1
    if A == 2:
        return 2

    return solution(A - 1) + solution(A - 2)

很显然, 一定会超时. 以5为例,

f(5)

f(4)+f(3)

[f(2)+f(3)] + [f(1)+f(2)]

[f(2)+f(1)+f(2)] + [f(1)+f(2)]

同学应该会发现,里面有很多重复的计算, 使得程序超时, 那么解决方法也很简单,就是将计算结果存储起来.
# 记忆数组
memo = {}

def solution(A: int) -> int:
    if A == 1:
        return 1
    if A == 2:
        return 1
    
    # 检查是否已经计算过
    if A in memo:
        return memo[A]
    
    # 递归调用并存储结果
    memo[A] = solution(A - 1) + solution(A - 2)
    return memo[A]
  • 记忆化递归可以显著提高递归方法的性能
还有一种更高效的解决方法是迭代法

也就是使用循环来计算每个月兔子对数,代码如下:

def solution(A: int) -> int:
    # 初始化前两个月的兔子对数
    if A == 1:
        return 1
    if A == 2:
        return 1
    
    # 初始化前两个月的兔子对数
    first, second = 1, 1
    
    # 从第3个月开始计算
    for _ in range(3, A + 1):
        # 计算当前月的兔子对数
        current = first + second
        # 更新前两个月的兔子对数
        first, second = second, current
    
    return second
使用迭代法的好处是:
时间复杂度

迭代方法:

  • 迭代方法通过一个循环从第3个月开始计算到第 A 个月
  • 每次循环中,我们只需要进行一次加法操作(current = first + second
  • 因此,循环总共执行 A - 2 次(从第3个月到第 A 个月)
  • 时间复杂度为 O(A)

递归方法:

  • 递归方法通过递归调用 solution(A - 1) 和 solution(A - 2) 来计算结果。
  • 每次递归调用都会产生两个新的递归调用,直到递归终止条件(A == 1 或 A == 2)。
  • 递归树的深度为 A,每个节点最多有两个子节点。
  • 因此,递归方法的时间复杂度为 O(2^A),这是一个指数级的时间复杂度。
空间复杂度

迭代方法

  • 迭代方法只需要存储前两个月的兔子对数(first 和 second)。
  • 无论 A 多大,我们只需要固定数量的额外空间来存储这些变量。
  • 空间复杂度为 O(1)

递归方法

  • 递归方法在每次递归调用时都会在栈上分配新的空间
  • 递归树的深度为 A,因此栈的最大深度也为 A
  • 空间复杂度为 O(A)
迭代方法在时间和空间上都比递归方法更高效,特别是对于较大的 A 值。因此,在实际应用中,迭代方法通常是更好的选择。

至此问题解决