算法原理
动态规划解决的一般步骤
1.状态表示
2.状态转移方程
3.初始化
4.填表顺序
5.返回值
本文通过4道例题总结利用动态规划的思想来解决斐波那契数列模型
题目
1. 第 N 个泰波那契数
1.题目描述
泰波那契序列定义如下:
T0 =0,T1=1,且在n>=0的条件下,Tn+3 = Tn+Tn+1+Tn+2
给你整数n,请你返回第n个泰波那契序列Tn的值
2.算法原理
这里我们按照解决动态规划的五步逐一分析
1.状态表示
什么是状态表示? dp表里面的值所表示的含义
如何得到状态表示?1.题目要求 2.经验+题目要求 3.分析问题的过程中,发现重复子问题
2.状态转移方程
dpi 等于什么?
这里题目已经告诉我们了 dpi = dpi-3 +dpi-2 +dpi-1
3.初始化
int\[\] dp = new intn+1
初始化的目的是为了保证填表的时候不越界
这道题目已经告诉我们T0 = 0 T1=1 T2=1
4.填表顺序 从左到右
为了填写当前状态的时候,所需要的状态已经计算过了
5.返回值
根据题目要求 返回dpn
3.代码实现
java
class Solution {
public int tribonacci(int n) {
if(n==0||n==1){
return n;
}
if(n==2){
return 1;
}
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = dp[2] =1;
for(int i=3;i<=n;i++){
dp[i] = dp[i-2] + dp[i-1] + dp[i-3];
}
return dp[n];
}
}
4.空间优化
动态规划一般使用滚动数组进行优化
需要的是连续的几个

java
class Solution {
public int tribonacci(int n) {
if(n==0||n==1){
return n;
}
if(n==2){
return 1;
}
int p = 0;
int q = 0;
int r = 1;
int s = 1;
for(int i = 3;i<=n;i++){
p = q;
q = r;
r = s;
s = p +q + r;
}
return s;
}
}
2.三步问题
1.题目描述
三步问题,有个小孩正在上楼梯 楼梯有n阶台阶,小孩一次可以上1阶,2阶或3阶 实现一种方法,计算小孩有多少种上楼方式,结果可能很大,你需要对结果模1000000007
2.算法原理
1.状态表示
根据经验(以某个位置为结尾或以某个位置为起点)+题目要求
dpi表示以i位置为结尾时,到达i位置时一共有多少种方法
2.状态转移方程
以i位置状态,最近的一步,来划分问题
i-1跳1步可以到达 i-2跳2步 i-3跳3步可以到达
dpi = dpi-1 + dpi-2 +dpi-3
3.初始化
dp1 = 1 dp2 = 2 dp3 = 4
4.填表顺序
从左往右
5.返回值
dpn;
3.代码实现
java
class Solution {
public int waysToStep(int n) {
int mod = (int)1e9+7;
if(n==1){
return 1;
}
if(n==2){
return 2;
}
if(n==3){
return 4;
}
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for(int i =4;i<=n;i++){
dp[i] = ((dp[i-3] + dp[i-2] )%mod+ dp[i-1])%mod;
}
return dp[n];
}
}
3.使用最小花费爬楼梯
1.题目描述
给你一个整数数组cost,其中costi是从楼梯第i个台阶向上爬需要支付的费用,一旦你支付此费用,即可选择向上爬一个或者两个台阶
你可以选择从下标为0或下标为i的台阶开始爬楼梯
请你计算并返回楼梯顶部的最低花费
我们结合实例1 cost=10,15,20 输出为14 从下标为1的台阶开始,支付15向上爬两个台阶,到达楼梯顶部,总花费15
可以知道楼顶是在数组的下一个位置
2.算法原理
解法一:以i位置为结尾
1.状态表示
经验+题目要求
以i位置为结尾,dpi表示到达i位置的最小花费
2.状态转移方程
用之前或者之后的状态,推导出dpi的值
根据最近的一步划分问题
dpi = min(dpi-1+costi-1+dpi-2+cost[i-2)
先到达i-1位置,支付costi-1 走一步花费 dpi-1+costi-1
先到达i-2位置,支付costi-2 走两步花费dpi-2+costi-2
3.初始化
dp = new intn+1
dp0 = 0;
dp1 = 0;
4.填表顺序
从左到右
保证填表的时候是不越界的
java
class Solution {
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int[] dp = new int[n+1];
for(int i = 2;i<=n;i++){
dp[i] = Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[n];
}
}
解法二:
1.状态表示 以i位置为起点
dpi表示:从i位置出发,到达楼顶,这是的最小花费
2.状态转移方程
dpi 支付costi 走一步 从i+1位置出发,到终点 dpi+1+costi
支付costi 走两步 从i+2位置出发到终点 dpi+2+costi
两者取最小值
3.初始化
和原始数组同等规模
dpn-1 = costn-1
dpn-2 = costn-2
4.填表顺序
从右向左
5.返回值
返回dp0 dp1 的最小值
java
class Solution {
public int minCostClimbingStairs(int[] cost) {
int n = cost.length;
int[] dp = new int[n];
dp[n-1] = cost[n-1];
dp[n-2] = cost[n-2];
for(int i = n-3;i>=0;i--){
dp[i] = Math.min(dp[i+1],dp[i+2])+cost[i];
}
return Math.min(dp[0],dp[1]);
}
}
4.解码方法
1.题目描述
一条包含字母A-Z的消息通过一下映射进行了编码:
'1'->'A' ... '26'->'Z'
然而,在解码已编码的消息时.你意识到有许多不同的方式来解码,因为有些编码被包含在其他编码中('2' 和'5' 与'25')
给定一个只含数字的非空字符串s,请计算并返回解码方法的总数,如果没有合法的方式解码整个字符串,返回0
(不能有前导0)
2.算法原理
1.状态表示
经验+题目要求
以某个位置为起点/结尾
dpi 表示:以i位置为结尾的时候,解码方法的总数
2.状态转移方程
根据最近的一步划分问题

dpi si单独解码 成功 1<=a<= 9 ->dpi-1 失败 0
si-1与si 解码 成功 10<=b*10+a<=26 dpi-2 失败 0
dpi = dpi-1 + dpi-2 (只有成功才能加)
3.初始化
dp0 = 1 (如果第一个字符是1<=a<=9) 不然就是0
dp1 = 0/1/2
处理边界问题以及初始化问题的技巧
加一个虚拟节点 注意:1.虚拟节点的值要保证后面的填表是正确的
2.下标的映射关系

dp0不能是0
这里是1
dp2 = dp1+dp0
4.填表顺序
从左向右
5.返回值
dpn
3.代码实现
java
class Solution {
public int numDecodings(String ss) {
int n = ss.length();
char[] s = ss.toCharArray();
int[] dp = new int[n+1];
dp[0] = 1;
if(s[0]!='0'){
dp[0] = 1;
dp[1] = 1;
}
for(int i = 2;i<=n;i++){
if(s[i-1]!='0'){
dp[i] += dp[i-1];
}
int tp = (s[i-2]-'0')*10 +s[i-1]-'0';
if(tp>=10&&tp<=26){
dp[i]+=dp[i-2];
}
}
return dp[n];
}
}