DP专题——斐波那契数列和路径问题
斐波那契数列
1.定义:
斐波那契数列的定义如下:
-
F(0) = 0
-
F(1) = 1
…….
-
F(n) = F(n-1) + F(n-2) (n ≥ 2)
2 .解法流程
我们可以看到,F(n)依赖F(n-1) 和 F(n-2),也就是第三个数依赖于他的前两个数。因此在此类问题中,我们常是建立一个dp表,根据状态转移方程来确定,怎么来填写这个表。通过存储子问题的解,来达到现问题的解。
(1) .状态表示: 常用dp[i], i 表示以某个位置结束或者开始,以[0,i] 区间上表示
(2) .状态转移方程(未填写表示因题而变)
(3) .初始化
(4) .填表顺序
(5) .返回值
3.例题
(1)
根据上诉思路,我们只需先求出 Tn-1 和 Tn-2 即可 ,代码如下:
class Solution {
public:
int tribonacci(int n) {
//1.创建dp表
vector<int> tmp(n+1);
//2.初始化
if(n == 0)return 0;
else if(n <= 2)return 1;
tmp[0] = 0,tmp[1] = 1,tmp[2] = 1;
//3.填表
for(int i = 3; i <= n; i++)
{
tmp[i] = tmp[i-1]+tmp[i-2]+tmp[i-3];
}
//4.确定返回值
return tmp[n];
}
};
路径问题
1.解法流程
(1) .状态表示: 常用dp[i][j], [i][j] 表示从 [i, j] 位置出发,或者从起始位置出发,到达 [i, j] 位置
(2) .状态转移方程(未填写表示因题而变)
(3) .初始化 : 可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:
i. 辅助结点⾥⾯的值要「保证后续填表是正确的」;
ii. 「下标的映射关系」。
(4) .填表顺序
(5) .返回值
2.例题
(1)
这道题如果我们定义成:从起点开始,到达 [i, j] 位置的时候,所需的最低初始健康点数,此时会有个问题,那就是后面的路我不一定走的下去了,因为我们当前的健康点数还会受到后⾯的路径的影响,可能扣血会扣到不足以支撑我们继续走下去。
**所以这题我们最后定义为从 [i, j] 位置出发,到达终点时所需的最低健康点数。**从 [i, j] 位置出发,下⼀步会有两种选择:
i. ⾛到右边,然后⾛向终点
ii. ⾛到下边,然后⾛向终点
上述分析可得,dp[i][j] = min(dp[i+1][j],dp[i][j+1]) - dungeon[i][j];
但此时有个特殊情况需要我们处理,那就是如果dungeon[i][j]的值过大,也就是某个格子里的血包特别大,这会使dp[i][j]小于0,这一是不符合我们的定义,二是会影响别的位置的格子的值,所以我们在这里要特判
dp[i][j] = max(1,dp[i][j]);//防止当前dungeon[i][j]是一个超大的值,导致dp[i][j]里面存放的是负数
代码如下:
class Solution {
public:
int calculateMinimumHP(vector<vector<int>>& dungeon) {
int m = dungeon.size(),n = dungeon[0].size();
vector<vector<int>> dp(m+1,vector<int>(n+1,INT_MAX));
//dp[i][j]表示,从i,j位置出发,到达终点,所需的最初始健康点数
dp[m-1][n] = 1;//到达终点时骑士至少需要 1 点生命值。
//dp[i][j]的值,等于下面和右边的dp的最小值,再减去当前dungeon[i][j]的值
for(int i = m-1 ; i >= 0; i--)
for(int j = n-1; j >= 0; j--)//从下往上,从右往左推
{
dp[i][j] = min(dp[i+1][j],dp[i][j+1]) - dungeon[i][j];
dp[i][j] = max(1,dp[i][j]);//防止当前dungeon[i][j]是一个超大的值,导致dp[i][j]里面存放的是负数
}
return dp[0][0];
}
};