文章目录
- 引言
- [一. 第n个泰波那契数](#一. 第n个泰波那契数)
-
- [1.1 题目链接:https://leetcode.cn/problems/n-th-tribonacci-number/description/](#1.1 题目链接:https://leetcode.cn/problems/n-th-tribonacci-number/description/)
- [1.2 题目分析:](#1.2 题目分析:)
- [1.3 思路讲解:](#1.3 思路讲解:)
- [1.4 代码实现:](#1.4 代码实现:)
- [二. 三步问题](#二. 三步问题)
-
- [2.1 题目链接:https://leetcode.cn/problems/three-steps-problem-lcci/description/](#2.1 题目链接:https://leetcode.cn/problems/three-steps-problem-lcci/description/)
- [2.2 题目分析:](#2.2 题目分析:)
- [2.3 思路讲解:](#2.3 思路讲解:)
- [2.4 代码实现:](#2.4 代码实现:)
- [三. 使用最小花费爬楼梯](#三. 使用最小花费爬楼梯)
-
- [3.1 题目链接:https://leetcode.cn/problems/min-cost-climbing-stairs/description/](#3.1 题目链接:https://leetcode.cn/problems/min-cost-climbing-stairs/description/)
- [3.2 题目分析:](#3.2 题目分析:)
- [3.3 思路讲解:](#3.3 思路讲解:)
- [3.4 代码实现:](#3.4 代码实现:)
- [四. 解码方法](#四. 解码方法)
-
- [4.1 题目链接:https://leetcode.cn/problems/decode-ways/description/](#4.1 题目链接:https://leetcode.cn/problems/decode-ways/description/)
- [4.2 题目分析:](#4.2 题目分析:)
- [4.3 思路讲解:](#4.3 思路讲解:)
- [4.4 代码实现:](#4.4 代码实现:)
- 小结

引言
上篇我们介绍了用动态规划解决斐波那契模型的相关代码,本篇我们将结合具体题目,进一步深化对于该算法的理解运用。
一. 第n个泰波那契数
1.1 题目链接:https://leetcode.cn/problems/n-th-tribonacci-number/description/
1.2 题目分析:
定义如下:
T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2
给定n,求Tn
1.3 思路讲解:
1. 状态表⽰:
这道题可以「根据题⽬的要求」直接定义出状态表⽰:
dp[i] 表⽰:第 i 个泰波那契数的值。
2. 状态转移⽅程:
题⽬已经⾮常贴⼼的告诉我们了:
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
3. 初始化:
从我们的递推公式可以看出, dp[i] 在 i = 0 以及 i = 1 的时候是没有办法进⾏推导的,因
为 dp[-2] 或 dp[-1] 不是⼀个有效的数据。
因此我们需要在填表之前,将 0, 1, 2 位置的值初始化。题⽬中已经告诉我们 dp[0] = 0,
dp[1] = dp[2] = 1 。
4. 填表顺序:
毫⽆疑问是「从左往右」。
5. 返回值:
应该返回 dp[n] 的值。
1.4 代码实现:
cpp
class Solution {
public:
int tribonacci(int n) {
//处理特殊情况
if(n==0) return 0;
if(n==1 ||n==2) return 1;
vector<int> f(n+1);
f[1]=f[2]=1;
for(int i=3;i<=n;i++)
{
f[i]=f[i-3]+f[i-2]+f[i-1];
}
return f[n];
}
};
代码分析:
以上代码存在大量冗余计算,实际上每次计算f[n]只需要使用临近前3个的值,因此可以进行
数组重排
的方式进行优化。
优化代码如下:
cpp
class Solution {
public:
int tribonacci(int n) {
//处理特殊情况
if(n==0) return 0;
if(n==1 ||n==2) return 1;
int a=0,b=1,c=1;
int d;
for(int i=3;i<=n;i++)
{
d=a+b+c;//前3个数相加
//更新
a=b;
b=c;
c=d;
}
return d;
}
};
二. 三步问题
2.1 题目链接:https://leetcode.cn/problems/three-steps-problem-lcci/description/
2.2 题目分析:
- 共有n节台阶
- 每次可以上1节,2节或3节台阶
- 求共有多少种上楼方法
- 如果结果很大,需要对结果取模
2.3 思路讲解:
首先枚举情况,观察规律。
n=1时,1种
n=2时,2种
n=3时,4种
n=4时,7种
n=5时,13种
以n=4时为例,将路程划分为0->x->4
- 0->1->4,0->1共有1种方法
- 0->2->4,0->2共有2种方法
- 0->3->4,0->3共有4种方法
因此总计有1+2+4=7种
状态方程为:dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
余下思路与上题基本一致
2.4 代码实现:
cpp
class Solution {
public:
int waysToStep(int n) {
//处理特殊情况
if(n==1) return 1;
if(n==2) return 2;
if(n==3) return 4;
vector<int> dp(n+1);
const int mod=1e9+7;
dp[1]=1,dp[2]=2,dp[3]=4;
for(int i=4;i<=n;i++)
{
dp[i]=((dp[i-3]%mod+dp[i-2]%mod)%mod+dp[i-1]%mod)%mod;
}
return dp[n];
}
};
三. 使用最小花费爬楼梯
3.1 题目链接:https://leetcode.cn/problems/min-cost-climbing-stairs/description/
3.2 题目分析:
- 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。
- 支付此费用,即可选择向上爬一个或者两个台阶。
- 可以从下标为0或1开始
- 爬到楼顶指的是cost[n],而非cost[n-1]
- 求取最小花费
3.3 思路讲解:
根据题目一时我们提供的思路,首先考虑构建状态方程
- 由于终点是在楼顶即结尾处,因此状态改变方向可设置为从左向右
- 每次可选择爬1个或者2个,因此相邻点之间的到达方式有两种
- dp[i-1] ->dp[i] 爬1节,花费为dp[i-1]+cost[i-1]
- dp[i-2] ->dp[i] 爬2节,花费为dp[i-2]+cost[i-2]
- 其中dp[i]表示到达该位置所需要的花费
因此状态方程可表示为dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])
3.4 代码实现:
cpp
class Solution {
public:
int minCostClimbingStairs(vector<int>& cost) {
int n = cost.size();
vector<int> dp(n + 1);
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];
}
};
四. 解码方法
4.1 题目链接:https://leetcode.cn/problems/decode-ways/description/
4.2 题目分析:
- 给你一个只含数字的 非空 字符串 s ,请计算并返回解码方法的总数 。
- 如果没有合法的方式解码整个字符串,返回 0。
- 可解码的数字只在区间1-26内,如果为06或60这种情况,则无法解码,直接返回0即可
4.3 思路讲解:
对于一个字符串s,
如果可以解码:
- s中的每个数字都可以被解码,或通过两两组合的方式进行解码
- 如果两种方式都不适用,则一定无法解码,直接返回0即可
首先考虑构建状态方程
,遍历方向为从左到右,因此结尾在右侧。
- dp[i]表示到该字符为止,存在的解码方法数
- 如果s[i]可以单独解码,即1<=s[i]<=9,那么dp[i]+=dp[i-1]
- 如果s[i[还可以和s[i-1]组合解码,例如1 3组合为13,那么dp[i]+=dp[i-2]
- 如果两种方式都不适用,则一定无法解码,直接返回0即可
4.4 代码实现:
cpp
class Solution {
public:
int numDecodings(string s) {
int n=s.size();
vector<int> dp(n+1);
dp[0]=1,dp[1]=s[1-1]!='0';//初始化,同时注意dp与源字符串对应的时候下标减1
for(int i=2;i<=n;i++)
{
//先判断能否单独编码
if(s[i-1]>='1' && s[i-1]<='9') dp[i]+=dp[i-1];
//再判断能否组合编码
int t=(s[i-2]-'0')*10+s[i-1]-'0';
if(t>=10 && t<=26) dp[i]+=dp[i-2];
}
return dp[n];
}
};
代码分析:
- 与之前不同的是,在此处将dp[0]设置为了1,而之前都是默认值0
- 这是因为在本题中dp[0]的值会对后续产生影响,因为如果可以组合编码,那么dp[2]的值会因为dp[0]的值而改变,后续的dp值也会被依次影响,
- 如果可以组合编码,那么此时dp[2]+=dp[0]时,方法应该加1,因此需要把dp[0]设置为1
小结
本篇关于动态规划解决斐波那契模型的讲解就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!