题目
给你有一个 非负 整数 k
。有一个无限长度的台阶,最低 一层编号为 0 。
Alice 有一个整数 jump
,一开始值为 0 。Alice 从台阶 1 开始,可以使用 任意 次操作,目标是到达第 k
级台阶。假设 Alice 位于台阶 i
,一次 操作 中,Alice 可以:
- 向下走一级到
i - 1
,但该操作 不能 连续使用,如果在台阶第 0 级也不能使用。 - 向上走到台阶
i + 2^jump
处,然后jump
变为jump + 1
。
请你返回 Alice 到达台阶 k
处的总方案数。
注意,Alice 可能到达台阶 k
处后,通过一些操作重新回到台阶 k
处,这视为不同的方案。
示例 1:
输入:k = 0
输出:2
解释:
2 种到达台阶 0 的方案为:
- Alice 从台阶 1 开始。
- 执行第一种操作,从台阶 1 向下走到台阶 0 。
- Alice 从台阶 1 开始。
- 执行第一种操作,从台阶 1 向下走到台阶 0 。
- 执行第二种操作,向上走 20 级台阶到台阶 1 。
- 执行第一种操作,从台阶 1 向下走到台阶 0 。
示例 2:
输入:k = 1
输出:4
解释:
4 种到达台阶 1 的方案为:
- Alice 从台阶 1 开始,已经到达台阶 1 。
- Alice 从台阶 1 开始。
- 执行第一种操作,从台阶 1 向下走到台阶 0 。
- 执行第二种操作,向上走 20 级台阶到台阶 1 。
- Alice 从台阶 1 开始。
- 执行第二种操作,向上走 20 级台阶到台阶 2 。
- 执行第一种操作,向下走 1 级台阶到台阶 1 。
- Alice 从台阶 1 开始。
- 执行第一种操作,从台阶 1 向下走到台阶 0 。
- 执行第二种操作,向上走 20 级台阶到台阶 1 。
- 执行第一种操作,向下走 1 级台阶到台阶 0 。
- 执行第二种操作,向上走 21 级台阶到台阶 2 。
- 执行第一种操作,向下走 1 级台阶到台阶 1 。
思路与分析
这个和阶梯问题很相似,最先的想法是递归,但因为jump会变,有可能到达同一个台阶会经历不同的jump,递推公式可能有点复杂。所以要另外找其他的方法。
向下走每次只能走1,可以视为对当前位置的微调,向上走是指数次的增加,所以当k很大的时候,决定能不能到达的因素应该是此时的jump。所以要找到每次jump和这次jump能不能到达k之间的关系。
对应于每一次的jump,能到达的最高的阶梯就是从不后退,而最低的阶梯就是穿插着一直后退,考虑到起始位置在x=1的地方,所以每次jump能到达的最高位置如下(1是起始下标)
最低位置是一样的,只要在每两次jump之间插入一个-1即可,每次jump到达的最低位置就是,在这个范围内只需要控制-1的次数就可以做到到达任意一级台阶。于是问题就转化成了组合问题。
而对于第k级台阶,样例中可以看到在不同jump的情况下都有可能达到,所以对于输入的k要看在不在多个jump的区间范围内,基于此代码就很清晰了。
具体实现
首先做个可以计算阶乘的函数
int comb(int n, int r) {
if (r == 0 || r == n) return 1;
r = r < n-r ? r : n-r;
long long result = 1;
for (int i = 0; i < r; ++i) {
result = result * (n - i) / (i + 1);
}
return result;
}
之后去算每次jump区间内是否有k,如果有的话需要退后几次,计算一个组合数找到全部退后的组合数就可以了。
求二的阶乘可以直接用位运算的方法,更快一点
int waysToReachStair(int k) {
long long int res = 0, jump = 0;
while ((1 << jump) - (jump + 1) <= k) {
if ((1 << jump) >= k) {
res += comb(jump + 1, (1 << jump) - k);
}
jump++;
}
return res;
}
全部的代码如下:
class Solution {
public:
int comb(int n, int r) {
if (r == 0 || r == n) return 1;
r = r < n-r ? r : n-r;
long long result = 1;
for (int i = 0; i < r; ++i) {
result = result * (n - i) / (i + 1);
}
return result;}
int waysToReachStair(int k) {
long long int res = 0, jump = 0;
while ((1 << jump) - (jump + 1) <= k) {
if ((1 << jump) >= k) {
res += comb(jump + 1, (1 << jump) - k);
}
jump++;
}
return res;
}
};
后话
又要开一个新坑喽~,用来记一下每天刷过的算法题,非科班畜生做这些题收获还是蛮多的,希望自己能坚持下来吧!