前言
兔群繁殖之谜在青训营X豆包MarsCode刷题打卡中分类为动态规划算法、简单级的问题。
何为动态规划算法?
动态规划(Dynamic Programming,DP)是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划常常适用于有重叠子问题和最优子结构性质的问题。
动态规划的性质
-
最优化原理:假设问题的最优解所包括的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
-
无后效性:即某阶段状态一旦确定。就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响曾经的状态。仅仅与当前状态有关。
-
有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到(该性质非动态规划适用的必要条件)
动态规划能解决的问题
零钱兑换、最长上升子序列、堆盒子、航线问题等问题。
但兔群繁殖之谜本质上也是一个经典的斐波那契数列问题,只不过稍微做了一些变形。
问题描述
生物学家小 R 正在研究一种特殊的兔子品种的繁殖模式。这种兔子的繁殖遵循以下规律:
- 每对成年兔子每个月会生育一对新的小兔子(一雌一雄)。
- 新生的小兔子需要一个月成长,到第二个月才能开始繁殖。
- 兔子永远不会死亡。
小 R 从一对新生的小兔子开始观察。他想知道在第 A 个月末,总共会有多少对兔子。
请你帮助小 R 编写一个程序,计算在给定的月份 A 时,兔子群体的总对数。
注意:
- 初始时有 1 对新生小兔子。
- 第 1 个月末有 1 对兔子:原来那对变成了成年兔子,并开始繁殖。
- 第 2 个月末有 2 对兔子:原来那 1 对成年兔子,繁殖了 1 对新生的小兔子。
- 从第 3 个月开始,兔子群体会按照上述规律增长。
输入
一个整数 A(1 ≤ A ≤ 50),表示月份数。
返回
一个长整数,表示第 A 个月末兔子的总对数。
测试样例
样例 1:
输入:A = 1
返回:1
样例 2:
输入:A = 5
返回:8
样例 3:
输入:A = 15
返回:987
对问题的理解
- 初始时有一对新生小兔子。
- 每对成年兔子每个月会生育一对新的小兔子。
- 新生的小兔子需要一个月成长,到第二个月才能开始繁殖。
- 兔子永远不会死亡。
确定数据结构
由于我们需要计算每个月的兔子个数,并且每个月的兔子个数依赖于前两个月的兔子个数,因此我们可以使用一个数组来存储每个月的兔子个数。
算法步骤
-
初始化一个数组
F,其中F[0]和F[1]都为 1,因为第 0 个月和第 1 个月都只有一对兔子。 -
从第 2 个月开始,每个月的兔子对数等于前两个月的兔子对数之和,即
F[i] = F[i-1] + F[i-2]。 -
最终返回
F[A],即第A个月的兔子对数。
递归与动态规划
- 可以使用递归的方式来计算,但为了避免重复计算,可以使用记忆化递归(即动态规划)来优化。
通过以上对问题的分析与理解,我们可以得到一个初步的框架,从而将思路和框架应用于编程,得到如下代码
问题解答
vis=[False for _ in range(76)]
F=[0 for _ in range(76)]
def calc_fbi(x):
if vis[x]==True:
return F[x]
if x==1 or x==0:
return 1
vis[x]=True
F[x]=calc_fbi(x-1)+calc_fbi(x-2)
return F[x]
def solution(A):
# Edit your code here
return calc_fbi(A)
if __name__ == "__main__":
# Add your test cases here
print(solution(5) == 8)
print(solution(1) == 1)
print(solution(15) == 987)
print(solution(50) == 20365011074)
编程问题的思路
-
明确问题。要清楚程序需要实现什么功能,要理解任务的目标、输入输出是什么。
-
其次是设计算法。这是解决问题的步骤,对于复杂的程序,可能需要考虑各种情况和逻辑分支。
-
接着是选择合适的数据结构。数据结构决定了数据如何存储和操作。如果要存储很多用户信息,可能会用到数组或者链表。
-
然后是进行代码编写。按照选定的编程语言的语法规则,把算法和数据结构用代码实现。
-
最后是测试与调试。运行程序,检查是否达到预期效果。如果有错误或者不符合要求的地方,要通过调试来找出问题所在,修改代码,直到程序正确运行。