动态规划(斐波那契数列模型)
1. 第 N 个泰波那契数
-
状态表示
dp[i]
所表示的含义,那么这个状态表示怎么来的那?题目要求、经验根据题目要求、分析问题的过程中发现重复子问题
-
状态转移方程
dp[i]等于什么。根据这题的题目我们发现
d[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]
-
初始化
保证填表的时候不越界
-
填表顺序
为了填写当前的状态,所需的状态已经完成了
-
返回值
这个题目的返回值当然就是
dp[n]
了
AC代码:
class Solution
{
public:
int tribonacci(int n)
{
if (n == 0 || n == 1) return n;
vector<int> dp(n + 1);
dp[0] = 0, dp[1] = 1, dp[2] = 1;
for (int i = 3; i <= n; i++)
{
dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3];
}
return dp[n];
}
};
优化之后:
AC代码:
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, d = 0;
for (int i = 3; i <= n; i++)
{
d = a + b + c;
a = b;
b = c;
c = d;
}
return d;
}
};
2. 三步问题
-
状态表示
dp[i]
表示:到达i位置时,一共有多少中走法 -
状态转移方程
-
初始化
-
填表
-
返回值
AC代码:
class Solution
{
const int mod = 1e9 + 7;
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);
dp[1] = 1, dp[2] = 2, dp[3] = 4;
for (int i = 4; i <= n; i++)
{
dp[i] = ((dp[i - 1] + dp[i - 2]) % mod + dp[i - 3]) % mod;
}
return dp[n];
}
};
这个题目三个值都加起来,再取模是不行的。这里我们没计算一次加法(乘法)都要进行一次取模运算
3. 使用最小花费爬楼梯
解法一
-
状态表示
dp[i]
表示到达i位置的最小花费 -
状态转移方程
-
初始化
-
填表顺序
-
返回值
AC代码:
class Solution
{
public:
int minCostClimbingStairs(vector<int>& cost)
{
int len = cost.size();
vector<int> dp(len + 1);
for (int i = 2; i <= len; i++)
{
dp[i] = min((dp[i - 1] + cost[i - 1]), (dp[i - 2] + cost[i - 2]));
}
return dp[len];
}
};
解法二
-
状态表示
dp[i]
从i位置出发,到达楼顶所需的最小花费 -
状态转移方程
-
初始化
dp[n - 1] = cost[n - 1], dp[n - 2] = cont[n - 2]
-
填表
从左往右
-
返回值
min(dp[0], dp[1])
AC代码:
class Solution
{
public:
int minCostClimbingStairs(vector<int>& cost)
{
int n = cost.size();
vector<int> dp(n);
dp[n - 1] = cost[n - 1], dp[n - 2] = cost[n - 2];
for (int i = n - 3; i >= 0; i--)
{
dp[i] = min((dp[i + 1] + cost[i]), (dp[i + 2] + cost[i]));
}
return min(dp[0], dp[1]);
}
};
4. 解码方法
-
状态表示
以i位置为结尾,有多少种解码方法
-
状态转移方程
-
初始化
dp[0] = 1 或者0
dp[1]
也有三种情况 0 、1 、2 -
填表
-
返回值
AC代码:
class Solution
{
public:
int numDecodings(string s)
{
int n = s.size();
vector<int> dp(n + 1);
if (s[0] == '0') dp[0] = 0;
else dp[0] = 1;
if (n == 1) return dp[0];
if (s[1] >= '1' && s[1] <= '9') dp[1] += dp[0];
int t = (s[0] - '0') * 10 + s[1] - '0';
if (t >= 10 && t <= 26) dp[1] += 1;
for (int i = 2; i < n; i++)
{
if (s[i] >= '1' && s[i] <= '9') dp[i] += dp[i - 1];
int t = (s[i - 1] - '0') * 10 + s[i] - '0';
if (t >= 10 && t <= 26) dp[i] += dp[i - 2];
}
return dp[n - 1];
}
};
还可以使用辅助节点的方式来处理这个题目的边界问题:
需要注意的是:
- 辅助节点里面的值要保证后续填表是正确的
- 下标的映射关系