动态规划算法

1137.第 N个泰波那契数

泰波那契序列 Tn 定义如下:

T0 = 0, T1 = 1, T2 = 1, 且在 n >= 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2

给你整数 n,请返回第 n 个泰波那契数 Tn 的值。

示例 1:

1
2
3
4
5
输入:n = 4
输出:4
解释:
T_3 = 0 + 1 + 1 = 2
T_4 = 1 + 1 + 2 = 4

示例 2:

1
2
输入:n = 25
输出:1389537

提示:

  • 0 <= n <= 37
  • 答案保证是一个 32 位整数,即 answer <= 2^31 - 1。

c语言版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
int tribonacci(int n) 
{
// T3 = T0 + T1 + T2;
// T4 = T1 + T2 + T3;
// T5 = T2 + T3 + T4;

// 1.状态表示,dp表。状态表示:dp表里面值的含义
// 怎么来? 1题目要求 2经验 + 题目要求 3分析问题发现重复子问题
// 2.状态转移方程 dp[i]等于什么? 公式
// 3.初始化:保证填表不越界。(T1, T2, T3已经指定了的)
// 4.填表的顺序。从左往右。
// 5.返回值。题目要求 + 状态表示。

// 1.dp表
// 2.初始化
// 3.填表
// 4.返回值

// 处理边界情况的
if (n < 0) return 0; // 或根据需求定义
if (n == 0) return 0;
if (n == 1 || n == 2) return 1;

// int* dp = (int*)malloc(sizeof(int) * (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];

// 滚动数组,优化空间。
int a = 0;
int b = 1;
int c = 1;
int d = 0;
for(int i = 3; i <= n; ++i)
{
d = a + b + c;
a = b;
b = c;
c = d;
}

return d;
}

c++版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution 
{
public:
int tribonacci(int n)
{
// 1.初始化的条件
if(n == 0) return 0;
if(n == 1 || n == 2) return 1;

// 2.dp表和dp表的初始化
vector<int> v;
v.reserve(n + 1);
v[0] = 0;
v[1] = 1;
v[2] = 1;

// 3.dp公式
for(int i = 3; i <= n; ++i)
{
v[i] = v[i-1] + v[i-2] + v[i-3];
}
return v[n];
}
};

面试题 08.01.三步问题

三步问题。有个小孩正在上楼梯,楼梯有 n 阶台阶,小孩一次可以上 1 阶、2 阶或 3 阶。实现一种方法,计算小孩有多少种上楼梯的方式。结果可能很大,你需要对结果模 1000000007。

示例 1:

1
2
3
输入:n = 3 
输出:4
说明:有四种走法

示例 2:

1
2
输入:n = 5
输出:13

提示:

  1. n 范围在[1, 1000000]之间

c代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
int waysToStep(int n) 
{
const int mod = 1000000007;
// 0 1 2 3 4
// 0-1:0-1 1
// 0-2:0-1,1-2; 0-2
// 0-3:0-1,1-2,2-3;0-1,1-3;0-2,2-3;0-3. 4
// 0 1 2 3 4
// 1 2 3
// 4 2 1


// dp[i] = dp[i-1] + dp[i-2] + dp[i-3]
// 1.初始化
// dp1,dp2,dp3
// 1 2 4
if(n == 1 || n == 2)
{
return n;
}
if(n == 3)
{
return 4;
}

// 1.创建dp表
// 2.初始化
int* p = (int*)malloc(sizeof(int) * (n + 1));
p[1] = 1;
p[2] = 2;
p[3] = 4;

// 3.填表
for(int i = 4; i <= n; ++i)
{
long long sum = (long long) p[i-1] + p[i-2] + p[i-3];
p[i] = (int)(sum % mod);
}

// 4.返回值
int result = p[n];
free(p); // 千万别忘!
return result;
}

c++代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int waysToStep(int n)
{
const int mod = 1000000007;
if (n == 0) return 0; // 根据题意可选
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)
{
// 使用 long long 防止中间运算溢出
long long sum = (long long)dp[i-1] + dp[i-2] + dp[i-3];
dp[i] = sum % mod;
}
return dp[n];
}

746.使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

1
2
3
4
5
输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。
- 支付 15 ,向上爬两个台阶,到达楼梯顶部。
总花费为 15 。(楼顶,数组之外的)

示例 2:

1
2
3
4
5
6
7
8
9
10
输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。
- 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
- 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
- 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
- 支付 1 ,向上爬一个台阶,到达楼梯顶部。
总花费为 6 。

提示:

  • 2 <= cost.length <= 1000
  • 0 <= cost[i] <= 999

c++代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution 
{
public:
int minCostClimbingStairs(vector<int>& cost)
{
// 1.状态表示
// 经验+题目要求。i位置结尾 + 题目要求。
// 到底i位置了。---》到底0....n位置最小的花费。
// 2.状态转移方程
// 用之前或者之后的状态,推导出dp[i]的值。
// 根据最近的一步u,划分问题。
// dp[i]---d[i-1]
// dp[i]---d[i-2]
// 1 2 3
// i-1
// i-2
// 3.初始化
// 填表的时候不越界的。
// 4.填表
// 从左往右
// 5.返回值
// dp[n]

// [10, 15, 20] 楼顶位置。
// 0 1 2 3 4 5 6 7 8 9
// [1,100,1,1,1,100,1,1,100,1] 楼顶的位置
// 1
// 1
// 100

// dp[0]花费0
// dp[1]花费0
// 方法1
// int n = cost.size();
// vector<int> dp(n + 1); // 起始位置,0,1,的费用都是零,然后跳到2。
// 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];
// 方法2
// 1.i位置为起点。
//

}
};

91.解码方法

一条包含字母 A-Z 的消息通过以下映射进行了 编码 :

1
"1" -> 'A' "2" -> 'B' ... "25" -> 'Y' "26" -> 'Z'

然而,在 解码 已编码的消息时,你意识到有许多不同的方式来解码,因为有些编码被包含在其它编码当中("2" 和 "5" 与 "25")。

例如,"11106" 可以映射为:

  • "AAJF" ,将消息分组为 (1, 1, 10, 6)
  • "KJF" ,将消息分组为 (11, 10, 6)
  • 消息不能分组为 (1, 11, 06) ,因为 "06" 不是一个合法编码(只有 “6” 是合法的)。

注意,可能存在无法解码的字符串。

给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。如果没有合法的方式解码整个字符串,返回 0。

题目数据保证答案肯定是一个 32 位 的整数。

示例 1:

1
2
3
输入:s = "12"
输出:2
解释:它可以解码为 "AB"(1 2)或者 "L"(12)。

示例 2:

1
2
3
输入:s = "226"
输出:3
解释:它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。

示例 3:

1
2
3
输入:s = "06"
输出:0
解释:"06" 无法映射到 "F" ,因为存在前导零("6" 和 "06" 并不等价)。

提示:

  • 1 <= s.length <= 100
  • s 只包含数字,并且可能包含前导零。

c++代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution 
{
public:
int numDecodings(string s)
{
// 1-A
// 26-Z
// 1.经验+题目要求
// i位置结尾。 s[i]和s[i-1]解码。
// s[i] 1-9 成功
// s[i-1] 10-26 成功

// dp[0]和dp[1]初始化
// 从左往右
// 返回值dp[i]的值

int n = s.size();
vector<int> dp(n);

dp[0] = (s[0] != '0');
if(n == 1) return dp[0];

if(s[0] != '0' && s[1] != '0') dp[1] += 1;

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] != '0') 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];
}
};

62.不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

image-20260830102814008

1
2
输入:m = 3, n = 7
输出:28

示例 2:

1
2
3
4
5
6
7
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

1
2
输入:m = 7, n = 3
输出:28

示例 4:

1
2
输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

C++代码

1