对于动态规划问题,五部曲
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
- 找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的
509. 斐波那契数
很简单的动规入门题,但简单题使用来掌握方法论的,还是要有动规五部曲来分析。
视频:手把手带你入门动态规划 | LeetCode:509.斐波那契数_哔哩哔哩_bilibili
斐波那契数 (通常用
F(n)
表示)形成的序列称为 斐波那契数列 。该数列由0
和1
开始,后面的每一项数字都是前面两项数字的和。也就是:F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定
n
,请计算F(n)
。时间复杂度:O(n) 空间复杂度:O(n)class Solution { public: int fib(int n) { if(n<=1)return n; vector<int>dp(n+1);//确定dp数组的含义 dp[0]=0; dp[1]=1; for(int i=2;i<=n;i++) { dp[i]=dp[i-1]+dp[i-2];//确定递推公式 } return dp[n]; } };
class Solution { public: int fib(int n) { if(n<=1)return n; int dp[2];//优化了空间复杂度为o(1) dp[0]=0; dp[1]=1; for(int i=2;i<=n;i++) { int sum=dp[0]+dp[1]; dp[0]=dp[1]; dp[1]=sum;//两个变量滚动更新 } return dp[1]; } };
时间复杂度:O(n) 空间复杂度:O(1)
递归解法
class Solution { public: int fib(int n) { if(n<=1)return n; return fib(n-1)+fib(n-2); } };
时间复杂度:O(2^n) 空间复杂度:O(n),算上了编程语言中实现递归的系统栈所占空间
70. 爬楼梯
视频:带你学透动态规划-爬楼梯(对应力扣70.爬楼梯)| 动态规划经典入门题目_哔哩哔哩_bilibili
假设你正在爬楼梯。需要
n
阶你才能到达楼顶。每次你可以爬
1
或2
个台阶。你有多少种不同的方法可以爬到楼顶呢?解题思路:自己先分析出递推公式,再寻找初始变量
class Solution { public: int climbStairs(int n) { if(n<2)return n; int pre1=1; int pre2=2; for(int i=3;i<=n;i++) { int sum=pre1+pre2; pre1=pre2; pre2=sum; } return pre2; } };
拓展:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入描述:输入共一行,包含两个正整数,分别表示n, m
输出描述:输出一个整数,表示爬到楼顶的方法数。
输入示例:3 2
输出示例:3
提示:
当 m = 2,n = 3 时,n = 3 这表示一共有三个台阶,m = 2 代表你每次可以爬一个台阶或者两个台阶。
此时你有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶段 1 阶 + 2 阶#include<iostream> #include<vector> using namespace std; int climbstairs(int n,int m) { vector<int>dp(n+1,0);//n为该函数的参数,应该在其内使用 dp[0]=1;//在0阶时只有保持不动一种方式 for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if((i-j)>=0) dp[i]+=dp[i-j]; } } return dp[n]; } int main() { int a,b; cin>>a>>b; cout<<climbstairs(a,b)<<endl; return 0; }
解题思路:举例子,若n=5,m=4,自己先模拟算出dp[5]=15.
明确dp[i]的定义,在于跳到第n阶时的几种方式
先求出dp[1],dp[2],dp[3],dp[4].从dp[1]跳表示一次可以跳4阶,dp[5]=dp[4]+dp[3]+dp[2]+dp[1]
dp[0]=1表示在第0阶时只有保持不动这一种方式
46. 使用最小花费爬楼梯
视频讲解:动态规划开更了!| LeetCode:746. 使用最小花费爬楼梯_哔哩哔哩_bilibili
给你一个整数数组
cost
,其中cost[i]
是从楼梯第i
个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。你可以选择从下标为
0
或下标为1
的台阶开始爬楼梯。请你计算并返回达到楼梯顶部的最低花费。
思路:重点还是抓住要达到dp[i],可从dp[i-1]跳,也可从dp[i-2]跳,在加上跳楼梯的花费取最小值
class Solution { public: int minCostClimbingStairs(vector<int>& cost) { vector<int>dp(cost.size()+1);//记录到第n的楼梯的最小花费,明确含义 dp[0]=0; dp[1]=0;//初始化 for(int i=2;i<=cost.size();i++)//从前向后遍历数组 { dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);//一级一级的跳和两级两级的跳进行比较 } return dp[cost.size()]; } };
时间复杂度:O(n) 空间复杂度:O(n)
代码优化,时间复杂度:O(n) 空间复杂度:O(1)
class Solution { public: int minCostClimbingStairs(vector<int>& cost) { int pre1=0; int pre2=0;//初始化 for(int i=2;i<=cost.size();i++)//从前向后遍历数组 { int cur=min(pre1+cost[i-1],pre2+cost[i-2]); //一级一级的跳和两级两级的跳进行比较 pre2=pre1; pre1=cur;//不断更新 } return pre1; } };