"又见背包,让我不再走得缓慢"
开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情
题目描述,有 级台阶,每一步可以上 级、 级、...、 级()。试问一共有多少种不同的方法可以爬到第 级台阶。
中规中矩的动态规划
多步爬楼梯问题这个问题是从 # 70. 爬楼梯 延伸出来的,与 # 377. 组合总和 Ⅳ 极其类似,翻译过来就是,从 序列中选择任意元素(可重复),所有元素之和等于 (因为一共有 级台阶,也是背包容量)的组合共有多少个? 顾名思义,这是一道 完全背包 的 排列 问题。
定义:1、元素和(背包容量)为 ;2、元素集合 。
NOTE: 数组从元素 开始是为了让元素的 与 相等,便于计算。
1、确定 dp 状态定义
定义 是凑成目标整数为 的排列个数,其中 。
2、确定 dp 状态方程
完全背包问题一维 模型(参考 # 重识背包问题(下))应为,。对于爬楼梯问题有, 恒等于 ,所以状态方程可以简化成,
其中,,如果 ,继续循环即可。
3、确定 dp 初始状态
当目标整数为 时,即 ,能凑成 的排列个数一定为 ,即不选择任何元素,故 。
4、确定遍历顺序
完全背包的排列问题,必须 先遍历背包,再遍历物品,故
-
外循环,从 遍历到
-
内循环,从 遍历到
5、确定最终返回值
依然要回归到 状态定义中,即 是凑成 的排列个数。
6、代码示例
/**
* 空间复杂度 O(n)
* 时间复杂度 O(m * n)
*/
function climbStairs(n: number, m: number): number {
const dp = new Array(n + 1).fill(0);
dp[0] = 1;
for (let j = 1; j <= n; j++) {
for (let i = 1; i <= m; i++) {
if (j - i >= 0) {
dp[j] += dp[j - i];
}
}
}
return dp[n];
};
当 时,问题退化成 # 70. 爬楼梯 ,内层循环变为,
for (let i = 1; i <= 2; i++) {
if (j - i >= 0) {
dp[j] += dp[j - i];
}
}
当 时,
-
,,
-
,,不执行,此时
递推形式
当 时,
-
,, ( 初始为 )
-
,,
递推形式 ,侧面证明 # 70. 爬楼梯 的最优子结构是完全正确的。