动态规划解三步问题:LeetCode 面试题 08.01. 三步问题
1. 题目链接
LeetCode 面试题 08.01. 三步问题
题目要求:小孩上楼梯,每次可以走1、2或3步,计算到达第 n
阶台阶的不同方式数,结果需对 1e9 + 7
取模。
2. 题目描述
- 输入:整数
n
,表示台阶数。 - 输出:不同的方式数(模
1e9 + 7
)。 - 约束条件:
1 ≤ n ≤ 1e6
- 结果可能超过
2^31 - 1
,需取模处理。
3. 示例分析
示例 1:
输入:n = 3
输出:4
解释:方式为 1+1+1
, 1+2
, 2+1
, 3
。
示例 2:
输入:n = 4
输出:7
解释:新增方式如 1+3
, 2+2
, 3+1
, 1+1+2
等。
4. 算法思路
动态规划递推
- 状态定义:
dp[i]
表示到达第i
阶的方式数。
- 递推公式:
dp[i] = dp[i-1] + dp[i-2] + dp[i-3]
。- 每次可从前3个台阶走1、2或3步到达当前台阶。
- 初始条件:
dp[1] = 1
,dp[2] = 2
,dp[3] = 4
。
空间优化
- 直接使用数组存储所有状态,空间复杂度为
O(n)
。 - 可优化为滚动变量(见注意事项)。
5. 边界条件与注意事项
- 边界处理:
n < 4
时直接返回初始值。
- 取模运算:
- 每次更新状态后需立即取模,防止溢出。
6. 代码实现与解析
class Solution {
public:
const int MOD = 1e9 + 7;
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++) {
// 使用 long 避免中间结果溢出
dp[i] = ( (long)dp[i-1] + dp[i-2] + dp[i-3] ) % MOD;
}
return dp[n];
}
};
代码解析
- 初始化处理:
- 直接处理
n ≤ 3
的边界情况,返回初始值。
- 直接处理
- 动态规划数组:
dp[i]
存储到达第i
阶的方式数,初始化前3项。
- 递推计算:
- 循环从
i = 4
开始,递推公式使用long
类型防止溢出。 - 每次计算后立即取模,确保结果合法。
- 循环从
- 返回值:
dp[n]
即为最终结果。
优化
- 滚动变量优化:
- 仅保留前3个状态,空间复杂度降至
O(1)
。
int waysToStep(int n) { if (n == 1) return 1; if (n == 2) return 2; if (n == 3) return 4; int a = 1, b = 2, c = 4, d; for (int i = 4; i <= n; i++) { d = ( (long)a + b + c ) % MOD; a = b; b = c; c = d; } return c; }
- 仅保留前3个状态,空间复杂度降至
- 统一取模逻辑:
- 所有中间操作统一用
long
类型,避免隐式溢出。
- 所有中间操作统一用
总结
通过动态规划递推解决三步问题,关键点在于状态转移和取模处理。代码使用 long
类型确保大数计算正确性,适用于 n ≤ 1e6
的场景。此方法可推广至类似递推问题(如泰波那契数),需注意数值溢出和空间优化。