【算法】动态规划:从斐波那契数列到背包问题
文章目录
1.斐波那契数列
斐波那契数列(Fibonacci sequence)是一个非常著名的数列,在数学中有着广泛的应用。这个数列的每一项都是前两项的和,通常从1开始。数列的前几项是这样的:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
数学上,斐波那契数列可以定义为如下递归形式:
F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2),对于 n ≥ 2 的整数。
依据这个递推式,我们将我们想要求的F(n)
转换为求F(n-1)
与F(n-2)
的值,这就是动态规划中的状态转移方程。简单来说,F(n)的得来是由F(n-1)
与F(n-2)
决定的。
那么F(n-1)
与F(n-2)
从何而来呢?
我们可以将它先计算好,存起来,用的时候直接拿来用就行。
这就是动态规划中的记忆化:
通过将已经计算过的子问题的结果存储起来,当再次遇到相同的子问题时,可以直接使用存储的结果,从而节省计算时间和资源。
所以如何用动态规划来求出F(n)
呢?
动态规划有三大步:
- 定义子问题
- 定义状态数组dp
- 定义状态转移方程
我们有dp[i]
来代表F(i)
的值
首先我们需要初始化dp
数组:
dp=[0]*(n+1)
然后我们先预处理一些值:
dp[1],dp[2]=1,1
这样之后,我们就可以得出dp[3]:
dp[3]=dp[2]+dp[1]
以此类推……
我们用一个for
循环就都可以表示出来:
for i in range(3,n+1):
dp[i]=dp[i-1]+dp[i-2]
最后dp[n]
就是我们要求的值了。
完整代码:
def fibonacci(n):
if n <= 1:return n
dp = [0] * (n + 1)
dp[1],dp[2] = 1,1
for i in range(3, n + 1):
dp[i] = dp[i - 1] + dp[i - 2]
return dp[n]
2.爬楼梯
了解这些之后,就可以做一些动态规划的题目了
而力扣的70. 爬楼梯与斐波那契数列非常相似
题目:
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:
输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
提示:
1 <= n <= 45
该题中,我们只能爬1阶或2阶,求爬到楼顶的方法数
那么首先我们想到该如何爬到楼顶
我们可以在离楼顶差一阶的时候再走1阶,差两阶的时候再走2阶。
由于本题要求爬到楼顶的方法数,所以我们可以定义子问题:求楼梯阶数为i
时,我们爬到楼顶所需的方法数。
依据子问题,我们可以定义状态数组:
用dp[i]
来表示当楼梯阶数为i
时,我们爬到楼顶所需的方法数。
而我们又知道:
i
是由i-1
再走1阶或者i-2
再走2阶而来。
所以dp[i](楼梯阶数为i时的方法数)
就等于dp[i-1](楼梯阶数为i-1时的方法数)
加上dp[i-2](楼梯阶数为i-2时的方法数)
所以我们就可以得出状态转移方程:
dp[i]=dp[i-1]+dp[i-2]
是不是很熟悉?
没错,这和斐波那契数列的状态转移方程一模一样
不过区别在于预处理的值不同:
dp[1],dp[2]=1,2
所以这道题也就解决了:
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
if n<=1:return n
dp=[0]*(n+1)
dp[1],dp[2]=1,2
for i in range(3,n+1):
dp[i]=dp[i-1]+dp[i-2]
return dp[n]
思考一:该题中的dp[i]=dp[i-1]+dp[i-2]
为什么是正确的?
我们将这题升级以下,可爬的阶梯数是不确定的,放在一个数组stairs
中,那么代码可以这样写:
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:type stairs: List[int]
:rtype: int
"""
dp=[0]*(n+1)
dp[0]=1
for i in range(1,n+1):
for j in stairs:
if i-j>=0:dp[i]+=dp[i-j]
return dp[n]
提示:如果感觉有点难理解请先看下一题
3.零钱转换
接下来我们