题目描述
有N级的台阶,你一开始在底部,每次可以向上迈最多K级台阶(最少1级),问到达第N级台阶有多少种不同方式。
输入格式
两个正整数N,K。
输出格式
一个正整数,为不同方式数,由于答案可能很大,你需要输出ans mod 100003后的结果。
说明
对于20%的数据,有N ≤ 100,
对于100%的数据,有N ≤ 100000
原题链接
链接: https://www.luogu.com.cn/problem/P1192
解题思路
假设一个台阶有4级,每次最多走3级。
如果第一步走1级,则剩下的走法和从0级到3级的走法是一样的。
如果第一步走2级,则剩下的走法和从0级到2级的走法是一样的。
如果第一步走3级,则剩下的走法和从0级到1级的走法是一样的。
way[n]为从第0级走到第n级台阶方法数
每次最多能上k级。
当第一步走1级,走到第n级的方法和走到n-1级的方法相同。
当第一步走2级,走到第n级的方法和走到n-2级的方法相同。
…
当第一步走k级,走到第n级的方法和走到n-k级的方法相同。
走到n级的所有方法就被分为了第一步走1级,2级…k级一共k种,走到n级的方法就等于先分别走1,2,3…k级的所有方法求和。
于是我们得到了下面的递推式
way[n]=way[n-1]+way[n-2]+…+way[n-k]
有了这个递推式后,我们只需求出way[1]——way[k],way[k+1]——way[n]都可由递推式获得。
核心代码
//先将way[]初始化为0;
for (int i = 1; i <= n; i++) //遍历递推
{
for (int m = 1; m <= k; m++) //阶数最少1到最大k阶依次累加
{
if (i >= m) //目标是i级,要走的步数不能比i大。
{
way[i] = way[i] + way[i - m];
way[i] = way[i] % 100003; //每次循环都作取模运算,防止数字太大数组溢出
}
}
}
完整代码:
#include<iostream>
using namespace std;
int main() {
int n, k;
int way[100010] = { 0 };
way[0] = 1; //起点到起点是1种方法。
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
for (int m = 1; m <= k; m++)
{
if (i >= m)
{
way[i] = way[i] + way[i - m];
way[i] = way[i] % 100003;
}
}
}
cout << way[n];
return 0;
}
心得体会
做半天,我好菜
这个问题用递归也可以解决,但是数据量太大递归必定爆栈。所以使用递推会是一个比较好的解法。但是此题最大台阶数有105个。在解此题时创建了way[100000]大小的数组,使用的空间较大。按照递推式来看,只要求出了way[1]——way[k]之后,way[k+1]——way[n]都可以使用之前的的数据进行求解。并且根据递推式,也只需要k+1大小的数组即可,way[n]可以由way[n-1],way[n-2]…way[n-k]一共k个数据求和得到,而way[n-k]可由way[n-k-1]——way[n-k-k]k个数据求得,我们只需要保存最近的k个数据就可以了。再额外留出一个数据存放最后要求那个数据即可。可用队列实现,最小值参与计算后出队,计算求得值入队,可以进一步节省空间。
部分思路借鉴:
链接:https://www.luogu.com.cn/problem/P1192