【动态规划基础】斐波那契类型

引入:斐波那契数列

斐波那契数列(Fibonacci sequence)是数学家斐波那契以研究兔子繁殖为例研究的数列,故称“兔子数列”,又称为黄金分割数列。数列如下:1,1,2,3,5,8,13.......。

兔子问题:假设一对初生兔子要一个月才到成熟期,而一对成熟兔子每月会生一对兔子,那么,由一对初生兔子开始,12 个月后会有多少对兔子呢?

可以发现兔子第一月有一对,第二月才成熟,第三月生兔子,此时兔子有两对,第四月第一对兔子又生一对兔子,三对兔子,第五月,两对成熟兔子生两对新兔子,有五对兔子.....每一个月的兔子总和为上一个月的兔子数量+上上个月的兔子数量,因为上一个月新生的兔子还不具备繁殖能力。如若没有需要成熟这一条件,则每一个月的兔子总和为生一个月兔子数量的两倍(成倍繁殖)。

兔子数列递推公式为F(n)=F(n-1)+F(n-2)(每一项为前两项的结果之和),在动态规划算法中体现了一种间隔性,是一维dp的一种体现。

解决斐波那契数列问题可以采用递归或者动态规划的思想,下面会介绍两者在时间复杂度上的区别。

动态规划问题的解题思路

动态规划问题(dp)是将原问题分解成相对简单的子问题,所有子问题只解决一次并存储子问题的解,本质上是一种记忆化搜索的思想。

思路如下:

先定义状态dp[i]的含义,然后写出状态转移方程(前两步很重要,也是动态规划问题容易遇到的瓶颈),进行状态的初始化,最后返回结果。

1、爬楼梯

题目:70. 爬楼梯 - 力扣(LeetCode)

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?(1<=n<=45)

错解

以下递归方法会超时(测试卡在n=44的情况),是因为作了多余的调用,进行了很多重复计算,(如最底层的clibstairs(1)就调用了很多遍),递归次数过多,时间复杂度过大,接近O(2^{n}),形成一种树状结构。

int climbStairs(int n) {
    if(n==1||n==0)return 1;
    else return climbStairs(n-1)+climbStairs(n-2);
    }  

题解:

每一个台阶视为一个状态,每个状态可由前面的状态推出。即若此时在第n层台阶,那么前一个状态为第n-1或n-2层台阶。

a[i]表示到达第i层的方法数,得状态转移方程a[i]=a[i-1]+a[i-2]。

动态规划用了将状态存储下来的思想,减少了不必要的函数调用,时间复杂度O(n)。

另外,此题可以不用数组a[]存储,改为使用变量a,b,c,这样可以降低空间复杂度。

int climbStairs(int n) {
     int a[100];
     a[0]=1;a[1]=1;
     for(int i=2;i<=n;++i){
a[i]=a[i-1]+a[i-2];
     }
return a[n];
    }  

2、使用最小花费爬楼梯

题目:746. 使用最小花费爬楼梯 - 力扣(LeetCode)

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。

示例 :

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。

题解:

此题为上题的变式。

dp[i]表示到达第i层需要的最低花费。

状态转移方程:dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);

int minCostClimbingStairs(vector<int>& cost) {
      int dp[1005];
      dp[0]=0;dp[1]=0;
      int n=cost.size();
      for(int i=2;i<=n;++i){
        dp[i]=min(dp[i-1] + cost[i-1],dp[i-2]+cost[i-2]);
      }
      return dp[n];
    }

3、删除并获得点数

题目:740. 删除并获得点数 - 力扣(LeetCode)

给你一个整数数组 nums ,你可以对它进行一些操作。每次操作中,选择任意一个 nums[i] ,删除它并获得 nums[i] 的点数。之后,你必须删除 所有 等于 nums[i] - 1 和 nums[i] + 1 的元素。开始你拥有 0 个点数。返回你能通过这些操作获得的最大点数。

示例 :

输入:nums = [3,4,2]
输出:6
解释:
删除 4 获得 4 个点数,因此 3 也被删除。
之后,删除 2 获得 2 个点数。总共获得 6 个点数。

题解:

用数组a[i]映射value为i的个数,若取数字nums[i],则为了尽量获得最大点数会将同样的数字取完,因为相邻大小的数字无法都取到,则需要找出最优取法。(和打家劫舍问题很像)

dp[i]表示扫描到数字i时能得到的最大点数,状态转移方程dp[i]=max(dp[i-1],dp[i-2]+i*a[i])。

int deleteAndEarn(vector<int>& nums) {
int maxx=0;//存储最大值
int dp[10005],a[10005];//a[i]数组记录有a[i]个i
for(int i=0;i<=10000;++i)a[i]=0;
for(int i=0;i<nums.size();++i){
a[nums[i]]++;
maxx=max(maxx,nums[i]);
}
dp[0]=0;dp[1]=a[1];
for(int i=2;i<=maxx;++i)dp[i]=max(i*a[i]+dp[i-2],dp[i-1]);
return dp[maxx];
    }

总结

斐波那契问题在动态规划上的体现为当前状态由前两个状态决定,时间复杂度O(n),空间复杂度若使用数组为O(n),使用滚动数组为O(1)(用变量的方式)。

备注

题目均来源于leetcode动态规划(基础版)专题。

更多例题:

198. 打家劫舍 - 力扣(LeetCode)

  • 48
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

197221何晓棠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值