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

本文总结了动态规划问题的学习思路,通过斐波那契数列和零钱兑换问题,探讨了动态规划的重叠子问题和最优子结构。文章详细分析了暴力递归、备忘录递归和DP数组迭代三种解法,并解释了如何通过状态转移方程优化解决方案。此外,还对比了爬楼梯问题和零钱兑换II的动态规划解法,阐述了如何根据问题特性调整状态数组和状态转移方程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

1、由斐波那契数列引入重叠子问题(斐波那契数列严格来说不是动态规划问题)
1)暴力递归
int fib(int n)
{
   
   
    if( n ==1 || n == 2)
        return 1;
    return fib(n-1) + fib(n-2);
}

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

画出递归树:
在这里插入图片描述

分析:因为计算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];
}

再次画出递归树:
在这里插入图片描述

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

在这里插入图片描述

递归算法的时间复杂度:

子问题个数×解决一个子问题需要的时间,子问题个数即为图中节点的总数,即数量与输入规模成正比,所以子问题个数为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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值