兔群繁殖之谜刷题思路分享 | 豆包MarsCode AI刷题

106 阅读3分钟

这道“兔群繁殖之谜”的问题,是我在参加厦大考研复试时C语言考试中遇到的一道题。当时,由于对动态规划(Dynamic Programming, DP)的理解还不够深入,我只能采用传统的数学推导方法,试图通过观察数列找到通项公式来解决问题。这种方法虽然也能得出答案,但计算的复杂度较高,并且在面对更加复杂的问题时显得力不从心。现在回过头来看,当时的方法确实显得有些笨拙。

在学习和掌握了动态规划算法之后,这道题目变得更加直观且易于求解。动态规划是一种将问题分解为子问题,通过保存子问题的计算结果,避免重复计算,从而提高效率的算法。它尤其适合求解具有“最优子结构”和“重复子问题”特性的问题,而“兔群繁殖之谜”就是这样一个典型的例子。

动态规划的核心思想

动态规划的关键在于明确状态的定义状态转移方程。状态的定义可以理解为“子问题的描述”,而状态转移方程则是各状态之间的逻辑联系。对于本题来说,我们需要深入分析问题的规律:

  • 兔子在出生后的第二个月才能开始繁殖,这意味着每只兔子至少需要一个月的“成长期”。
  • 每个月的兔子状态可以根据上一个月的兔子状态推导出来。

问题状态的定义

为了更直观地解决问题,我们可以将兔子的状态划分为三种:

  1. 状态0:新出生的兔子。这些兔子在当前月刚出生,还不能繁殖。
  2. 状态1:成长期的兔子。这些兔子已经存活了一个月,下个月将开始繁殖。
  3. 状态2:成熟的兔子。这些兔子已经具备繁殖能力,每个月都可以生出新的兔子。

我们用三个变量表示这三种状态下的兔子数量:dp[0]表示新出生的兔子数量,dp[1]表示成长期的兔子数量,dp[2]表示成熟的兔子数量。

动态规划的推导公式

基于以上状态的定义,我们可以推导出状态转移方程:

  1. 新出生的兔子(dp[0]) :每个月,新出生的兔子数量等于上个月成熟兔子的数量,即 dp[0] = dp[2]
  2. 成长期的兔子(dp[1]) :上个月新出生的兔子进入成长期,即 dp[1] = dp[0]
  3. 成熟的兔子(dp[2]) :上个月的成熟兔子和成长期的兔子都在本月成为成熟兔子,即 dp[2] = dp[1] + dp[2]

通过这三个状态转移公式,我们可以从初始状态逐步迭代出每个月兔子总数量的变化。

动态规划的实现

在实现时,我们只需初始化第一个月的兔子状态,假设初始有一只成熟兔子:dp[0] = 0, dp[1] = 0, dp[2] = 1,然后按月迭代更新状态即可。最终,任意一个月的兔子总数为 dp[0] + dp[1] + dp[2]

​
public class Main {
    public static long solution(int A) {
        // Edit your code here
        //动态规划算法
        //三个状态 0 新出生 1 代表活了一个月 接下来可以生 2 代表一定可以生
        Long[][] dp = new Long[A+1][3];
        dp[1][0] = 0L;
        dp[1][1] = 0L;
        dp[1][2] = 1L;
        for(int i=2;i<A+1;i++){
            dp[i][0] = dp[i-1][1]+dp[i-1][2];
            dp[i][1] = dp[i-1][0];
            dp[i][2] = dp[i-1][1]+dp[i-1][2];
        }
​
        return dp[A][0]+dp[A][1]+dp[A][2];
    }
​
    public static void main(String[] args) {
        // Add your test cases here
        System.out.println(solution(1) == 1L);
        System.out.println(solution(5) == 8L);
        System.out.println(solution(15) == 987L);
    }
}
​
​