Files
leetcode-master/problems/0070.爬楼梯完全背包版本.md
2025-05-19 17:11:04 +08:00

253 lines
7.7 KiB
Markdown
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

* [做项目多个C++、Java、Go、测开、前端项目](https://www.programmercarl.com/other/kstar.html)
* [刷算法(两个月高强度学算法)](https://www.programmercarl.com/xunlian/xunlianying.html)
* [背八股40天挑战高频面试题](https://www.programmercarl.com/xunlian/bagu.html)
# 70. 爬楼梯(进阶版)
[卡码网57. 爬楼梯](https://kamacoder.com/problempage.php?pid=1067)
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬至多m (1 <= m < n)个台阶你有多少种不同的方法可以爬到楼顶呢
注意给定 n 是一个正整数
输入描述输入共一行包含两个正整数分别表示n, m
输出描述输出一个整数表示爬到楼顶的方法数
输入示例3 2
输出示例3
提示
m = 2n = 3 n = 3 这表示一共有三个台阶m = 2 代表你每次可以爬一个台阶或者两个台阶
此时你有三种方法可以爬到楼顶
* 1 + 1 + 1 阶段
* 1 + 2
* 2 + 1
## 思路
之前讲这道题目的时候因为还没有讲背包问题所以就只是讲了一下爬楼梯最直接的动规方法斐波那契)。
**这次终于讲到了背包问题,我选择带录友们再爬一次楼梯!**
这道题目 我们在[动态规划:爬楼梯](https://programmercarl.com/0070.爬楼梯.html) 中已经讲过一次了这次我又给本题加点料力扣上没有原题所以可以在卡码网[57. 爬楼梯](https://kamacoder.com/problempage.php?pid=1067)上来刷这道题目
我们之前做的 爬楼梯 是只能至多爬两个台阶
这次**改为一步一个台阶两个台阶三个台阶.......直到 m个台阶问有多少种不同的方法可以爬到楼顶呢**
这又有难度了这其实是一个完全背包问题
1阶2阶.... m阶就是物品楼顶就是背包
每一阶可以重复使用例如跳了1阶还可以继续跳1阶
问跳到楼顶有几种方法其实就是问装满背包有几种方法
**此时大家应该发现这就是一个完全背包问题了!**
和昨天的题目[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)基本就是一道题了
动规五部曲分析如下
1. 确定dp数组以及下标的含义
**dp[i]爬到有i个台阶的楼顶有dp[i]种方法**
2. 确定递推公式
[动态规划494.目标和](https://programmercarl.com/0494.目标和.html) [动态规划518.零钱兑换II](https://programmercarl.com/0518.零钱兑换II.html)[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)中我们都讲过了求装满背包有几种方法递推公式一般都是dp[i] += dp[i - nums[j]];
本题呢dp[i]有几种来源dp[i - 1]dp[i - 2]dp[i - 3] 等等dp[i - j]
那么递推公式为dp[i] += dp[i - j]
3. dp数组如何初始化
既然递归公式是 dp[i] += dp[i - j]那么dp[0] 一定为1dp[0]是递归中一切数值的基础所在如果dp[0]是0的话其他数值都是0了
下标非0的dp[i]初始化为0因为dp[i]是靠dp[i-j]累计上来的dp[i]本身为0这样才不会影响结果
4. 确定遍历顺序
这是背包里求排列问题**12 21 步都是上三个台阶但是这两种方法不一样**
所以需将target放在外循环将nums放在内循环
每一步可以走多次这是完全背包内循环需要从前向后遍历
5. 举例来推导dp数组
介于本题和[动态规划377. 组合总和 Ⅳ](https://programmercarl.com/0377.组合总和Ⅳ.html)几乎是一样的这里我就不再重复举例了
以上分析完毕C++代码如下
```CPP
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
while (cin >> n >> m) {
vector<int> dp(n + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; i++) { // 遍历背包
for (int j = 1; j <= m; j++) { // 遍历物品
if (i - j >= 0) dp[i] += dp[i - j];
}
}
cout << dp[n] << endl;
}
}
```
* 时间复杂度: O(n * m)
* 空间复杂度: O(n)
代码中m表示最多可以爬m个台阶代码中把m改成2就是 力扣70.爬楼梯的解题思路
**当然注意 力扣是 核心代码模式卡码网是ACM模式**
## 总结
**本题看起来是一道简单题目,稍稍进阶一下其实就是一个完全背包!**
如果我来面试的话我就会先给候选人出一个 本题原题看其表现如果顺利写出来进而在要求每次可以爬[1 - m]个台阶应该怎么写
顺便再考察一下两个for循环的嵌套顺序为什么target放外面nums放里面
这就能考察对背包问题本质的掌握程度候选人是不是刷题背公式一眼就看出来了
这么一连套下来如果候选人都能答出来相信任何一位面试官都是非常满意的
**本题代码不长题目也很普通但稍稍一进阶就可以考察完全背包而且题目进阶的内容在leetcode上并没有原题一定程度上就可以排除掉刷题党了简直是面试题目的绝佳选择**
## 其他语言版本
### Java:
```Java
import java.util.Scanner;
class climbStairs{
public static void main(String [] args){
Scanner sc = new Scanner(System.in);
int m, n;
while (sc.hasNextInt()) {
// 从键盘输入参数,中间用空格隔开
n = sc.nextInt();
m = sc.nextInt();
// 求排列问题,先遍历背包再遍历物品
int[] dp = new int[n + 1];
dp[0] = 1;
for (int j = 1; j <= n; j++) {
for (int i = 1; i <= m; i++) {
if (j - i >= 0) dp[j] += dp[j - i];
}
}
System.out.println(dp[n]);
}
}
}
```
### Python3
```python3
def climbing_stairs(n,m):
dp = [0]*(n+1) # 背包总容量
dp[0] = 1
# 排列题,注意循环顺序,背包在外物品在内
for j in range(1,n+1):
for i in range(1,m+1):
if j>=i:
dp[j] += dp[j-i] # 这里i就是重量而非index
return dp[n]
if __name__ == '__main__':
n,m = list(map(int,input().split(' ')))
print(climbing_stairs(n,m))
```
### Go
```go
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func climbStairs(n int, m int) int {
dp := make([]int, n+1)
dp[0] = 1
for i := 1; i <= n; i++ {
for j := 1; j <= m; j++ {
if i-j >= 0 {
dp[i] += dp[i-j]
}
}
}
return dp[n]
}
func main() {
// 读取输入n,m
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
nv := strings.Split(input, " ")
n, _ := strconv.Atoi(nv[0])
m, _ := strconv.Atoi(nv[1])
result := climbStairs(n, m)
fmt.Println(result)
}
```
### JavaScript:
```javaScript
var climbStairs = function (n) {
let dp = new Array(n + 1).fill(0);
dp[0] = 1;
// 排列题,注意循环顺序,背包在外物品在内
for (let j = 1; j <= n; j++) {//遍历背包
for (let i = 1; i <= 2; i++) {//遍历物品
if (j - i >= 0) dp[j] = dp[j] + dp[j - i];
}
}
return dp[n];
}
```
### TypeScript
```typescript
var climbStairs = function (n: number): number {
let dp: number[] = new Array(n + 1).fill(0);
dp[0] = 1;
for (let j = 1; j <= n; j++) {//遍历背包
for (let i = 1; i <= 2; i++) {//遍历物品
if (j - i >= 0) dp[j] = dp[j] + dp[j - i];
}
}
return dp[n];
}
```
### Rust: