前言
本题是记录 七十【70. 爬楼梯】的扩展题。继续。
一、题目阅读
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬至多m (1 <= m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
输入描述
输入共一行,包含两个正整数,分别表示n, m
输出描述
输出一个整数,表示爬到楼顶的方法数。
输入示例
3 2 代表共有3个台阶,每次至多走2步。
输出示例
3 到达第3层台阶,有3种方案。
提示信息
数据范围:
1 <= m < n <= 32;
当 m = 2,n = 3 时,n = 3 这表示一共有三个台阶,m = 2 代表你每次可以爬一个台阶或者两个台阶。
此时你有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶段
1 阶 + 2 阶
2 阶 + 1 阶
二、尝试实现
思路
- 参考说这是完全背包问题(70. 爬楼梯(进阶版)参考链接),此时还没有学背包问题怎么做,先分析题目。找状态转移公式,确定dp含义和下标含义。
- dp数组如果还用一维已经不够了,因为有两个参数int n代表到第几层;int m指明每步至多走多少。所以需要二维数组,dp[i][j]代表到第i层台阶,每步至多走j步,有dp[i][j]种方案。
- 开始推导状态转移公式。
所以对应的dp数组如下:
- 确定遍历顺序:外层循环是i,代表每一层,在第几层台阶上,从上到下;内层循环是j,代表每一列,一步最多走j个台阶,从左到右。(方向是由递推公式得来)
- 数组的规律有:
- 当 j ==1时,可以直接赋值1;
- 当j == i时,dp等于左边的前一个dp[i][j-1] +1.
- 当 j > i时,dp等于左边的值。dp[i][j] == dp[i][j-1];
- 当j < i 时,要同一列倒数j个数相加。
- 数组初始化:第一行特殊,所以初始化始终为1。
- 下标0行0列没有含义。
代码实现
#include <iostream>
#include <vector>
using namespace std;
int climbstair(int n,int m){
//定义dp[i][j]数组,dp[i][j]到达第i层,可以至多爬j个台阶,有多少种方案。
vector<vector<int>> dp(n+1,vector<int> (m+1,0));
//初始化
for(int j = 1;j <= m;j++){
dp[1][j] = 1;
}
//遍历顺序
for(int i = 2;i <=n;i++){//代表第几层
for(int j = 1;j <= m;j++){
if(j ==1) dp[i][j] = 1;
else if(j == i) dp[i][j] = dp[i][j-1]+1;
else if(j > i) dp[i][j] = dp[i][j-1];
else{
//递推公式
for(int k = 1;k <= j;k++){
dp[i][j] += dp[i-k][j];
}
}
}
}
return dp[n][m];
}
int main(){
int n,m;
cin>>n>>m;
int result = climbstair(n,m);
cout<<result<<endl;
return 0;
}
总结
这道题目在后面背包问题学习中再来继续练习。
代码只是用动态规划五部曲完成,硬推状态转移公式,耗时较久,并没有映射到参考说的完全背包问题上。
动态规划还是渐进练习。
(欢迎指正,转载标明出处)