兔群繁殖之谜 | 豆包MarsCode AI刷题

57 阅读4分钟

题目分析

通过兔子繁殖遵循的规律进而计算出在特定月份兔子群体的总对数。
每一个月生育一对,并且新生的兔子需要到第二个月才能繁殖,通过注意可以推演出兔群的繁殖过程。

思路分析

首先想到可以使用滚动数列的方法进行实现,遍历数组,并且计算之前累加的数组进而得到当前月份兔子的总数,并且由于兔子并不会死亡,所以只需要在上个月繁殖的数量加上在当前月成年的兔子的数量便能够得到当前月份的兔子的繁殖数量,进而算出当前的兔子的总数。

long result = 1; 
long rabbit = 0; 
long newrabbit = 1; 
for (int i = 0; i < A; i++) 
{ long p = newrabbit; 
result = rabbit + result; 
newrabbit = rabbit;
rabbit = rabbit + p;
} 
return result;

代码部分,使用临时变量p来保存上个月的新兔子的数量,将rabbit即繁殖的新兔子加上之前的兔子总量,并且把当前新兔子设定为newrabbit,最后将rabbit加上上个月新兔子的数量表示下个月能够进行繁殖的兔子数量。然后通过月份的迭代,直接算出兔子的总数。
与此同时,这个问题的本质是斐波那契数列问题,可以考虑使用动态规划问题。
根据题意,我们有以下递推关系:

  • f(1) = 1(初始时有 1 对小兔子)
  • f(2) = 2(第 2 个月时,成年兔子繁殖了新兔子)
  • 对于 n ≥ 3,兔子的数量是前两个月兔子数量之和,即: f(n)=f(n−1)+f(n−2)f(n) = f(n-1) + f(n-2)f(n)=f(n−1)+f(n−2)
public static int calculateRabbitPairs(int A) 
{ if (A == 1) { return 1;} 
if (A == 2) { return 2;
}
int[] dp = new int[A + 1];
dp[1] = 1; 
dp[2] = 2;
for (int i = 3; i <= A; i++) 
{dp[i] = dp[i - 1] + dp[i - 2]; 
return dp[A]; 
}

通过dp[]来存储每个月对应的兔子的数量,先通过数学分析得到函数月份之间的关系,之后直接利用函数关系进行迭代更新,采用动态规划的办法完成。

比较分析

1. 时间复杂度

  • 方法一(动态规划数组实现)
    时间复杂度为 O(A) ,因为我们需要通过循环计算从第 3 个月到第 A 个月的兔子对数,每个计算步骤是常数时间操作。
  • 方法二(滚动数组实现)
    时间复杂度也是 O(A) ,同样是需要通过循环计算兔子对数,但是这里用两个变量来存储前两个月的结果,因此每次迭代仍然是常数时间操作。

2. 空间复杂度

  • 方法一(动态规划数组实现)
    空间复杂度为 O(A) ,因为我们使用了一个数组 dp 来存储每个月兔子的数量。即使我们只关心最后一个月的结果,也需要保存每个月的计算结果。
  • 方法二(滚动数组实现)
    空间复杂度为 O(1) ,因为我们只用了常数个变量(rabbitnewrabbitresult)来计算兔子数量,不需要存储每个月的结果。
总结:

方法二在空间复杂度上更优,空间复杂度 O(1) ,而方法一是 O(A) 。因此,方法二在处理非常大的 A 时更加节省内存。

3. 代码简洁性和可读性

  • 方法一(动态规划数组实现)

    • 代码较为直观,通过 dp 数组存储每个月的兔子对数,容易理解,符合常规的动态规划问题解决方案。
    • dp[i] = dp[i-1] + dp[i-2] 的递推关系很清晰,容易理解和追踪。
  • 方法二(滚动数组实现)

    • 代码稍微复杂一些,使用了 rabbitnewrabbit 来保存前两个月的结果,并且用 result 累加每个月的兔子数量。
    • 这个方法通过滚动数组(即通过两个变量替代整个数组)来优化空间,但代码的可读性可能稍差一些,尤其是在更新变量 rabbitnewrabbit 时,可能不太容易看出它是如何表示兔子数列的。