【算法】动态规划:从斐波那契数列到背包问题

【算法】动态规划:从斐波那契数列到背包问题

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)呢?

动态规划有三大步:

  1. 定义子问题
  2. 定义状态数组dp
  3. 定义状态转移方程

我们有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 阶你才能到达楼顶。

每次你可以爬 12 个台阶。你有多少种不同的方法可以爬到楼顶呢?

示例 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.零钱转换

接下来我们

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值