欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

动态规划问题(1) 之 斐波那契数列

程序员文章站 2024-03-19 21:18:58
...

动态规划问题的学习思路总结

1、由斐波那契数列引入重叠子问题(斐波那契数列严格来说不是动态规划问题)

1)暴力递归

int fib(int n)
{
    if( n ==1 || n == 2)
        return 1;
    return fib(n-1) + fib(n-2);
}

时间复杂度为:O(2^n)

画出递归树:

动态规划问题(1) 之 斐波那契数列

分析:因为计算fib(20),就需要计算fib(19)和fib(18),计算fib(19)有需要计算fib(18)和fib(17),最后到fib(2)和fib(1)。很明显算法低效的原因是存在大量的重复计算。这就引入了动态规划问题的第一个性质:重叠子问题

2)用带备忘录的递归解法

因为重复计算造成了效率的低下,那我们考虑定义一个备忘录,每次计算完子问题的答案后先别急着返回,而是先记录到备忘录中再返回;每次遇到一个子问题先去备忘录中查看是否已经有记录,如果发现之前已经计算过这个问题,直接把答案拿来使用即可,不用再花时间去计算。这里使用vector 数组维护备忘录。

int fib(int n)
{
    if(n < 1)
        return 0;
    vector<int> memo(n+1, 0); //备忘录先初始化为0
    return helper(memo, n); //从下标1开始使用,下标0不使用,直到下标n。共n个元素
}
​
int helper(vector<int> & memo, int n)
{
    if(n ==1 || n ==2)
        return 1;
    if(memo[n] != 0) return memo[n];
    memo[n] = helper(memo, n-1) + helper(memo, n-2);
    return memo[n];
}

再次画出递归树:

动态规划问题(1) 之 斐波那契数列

我们可以发现,相比暴力递归,备忘录的递归对之前的递归树进行了剪枝操作,极大减少了子问题个数。

动态规划问题(1) 之 斐波那契数列

递归算法的时间复杂度:

子问题个数×解决一个子问题需要的时间,子问题个数即为图中节点的总数,即数量与输入规模成正比,所以子问题个数为O(n),解决一个子问题的时间为O(1),所以此算法的时间复杂度为O(n)。效率已经和迭代的动态规划解法一样了。此算法我们是自顶向下的分解问题规模,直到最底层的fib(1)和fib(2),然后再从下往上逐层返回答案。而自底向上则正好相反,是从最底层的fib(1)和fib(2)向上推,直到fib(20),这就是动态规划的思路。

3)用 dp 数组迭代解法

我们将解法2中的备忘录独立出来成为一张表,在这张dp表上完成自底向上的推算,即可解决问题。

int fib(int n)
{
    vector<int> dp(n+1, 0); //初始化为0
    dp[1] = 1;
    dp[2] = 2;
    for(int i=3; i<=n; ++i)
    {
        dp[i] = dp[i-1] + dp[i-2];
    }
    return dp[n];
}

因为我们计算一个状态只需要记录前两个记录即可,而不需要存储所有的状态,所以还可以进一步优化空间复杂度:

int fib(int n)
{
    if(n==1 || n==2)
        return 1;
    int pre = 1;
    int cur = 1;
    int sum;
    for(int i=3; i<=n; ++i)
    {
        sum  = pre + cur;
        pre = cur;
        cur = sum;
    }
    return cur;
}

画图帮助理解:

动态规划问题(1) 之 斐波那契数列

这里引出 状态转移方程这个名词,实际上就是描述问题结构的数学形式:

                                                       动态规划问题(1) 之 斐波那契数列

很容易可以发现 状态转移方程 直接代表着暴力解法。

动态规划问题最困难的就是写出状态转移方程,即暴力解法,优化方法无非是用 备忘录或者DP table。

 

本文是参考labuladong公众号的文章自己记录的学习笔记,仅供大家参考和自己再次复习,有兴趣的同学可以关注公众号labuladong进一步学习。有很多优秀的文章可以学习。