1. 题目来源
2. 题目解析
很不错的题目哈。如果想到是 dp 的话,确实一时半会还弄不出来,因为这个数组范围不确定,都不知道开多大。
记忆化搜索的话,配合哈希表存储状态,是可以做,但也需要一定的技巧,如灵神记忆化搜索中写的将状态压缩到 LL 中。一些边界情况又要考虑清楚。估计也就是练练手就忘的程度。
正解还得是组合数学。
思路:
- 因为操作1 限制较大。操作2 没有限制,简单推一下公式就可以知道如果做 n 次操作 2,就可以向前跳 2 n − 1 2^n-1 2n−1 个台阶。此为上界。
- 此时可以操作 n+1 次操作 1,即可以回跳 2 n − 1 − n − 1 = 2 n − n − 2 2^n-1-n-1=2^n-n-2 2n−1−n−1=2n−n−2 个台阶位置。此为下界。
- 且因为初始化在台阶1上,所以,n 次操作可到达的台阶位置为 [ 2 n − n − 1 , 2 n ] [2^n-n-1, 2^n] [2n−n−1,2n]。
- 那么如果当 k 在这个台阶位置内的话,只需要 2 n − k 2^n-k 2n−k 次操作1,即可到达 k 位置。这就是一种方案。
- 此时相当于有 n + 1 n+1 n+1 个空位,选择其中的 2 n − k 2^n-k 2n−k 个空位进行填充的方案数。即为组合数 ( 2 n − k n + 1 ) \binom{2^n-k}{n+1} (n+12n−k)
这里 k 取值范围较小,所以可以在 int 范围内枚举完毕。但还是需要注意在枚举 2^n 的时候爆 int 的问题…乘到最后一个位置的时候,将爆 int…
- 时间复杂度: O ( l o g k ) O(logk) O(logk)
- 空间复杂度: O ( 1 ) O(1) O(1)
class Solution {
public:
typedef long long LL;
int C(int a, int b) { // 组合数简单计算公式
LL res = 1;
for (int i = 1; i <= b; i ++ ) res = res * (a - i + 1) / i;
return res;
}
int waysToReachStair(int k) {
int res = 0;
LL pow2 = 1; // 注意在这开 LL 就行了,不然下面越界
// 枚举操作 2 的次数。k 需要落在 2^n-n-1 ~ 2^n 这个范围内
// 其中,操作 1 的次数即为 2^n-k 次
for (int i = 0; i <= 30; i ++ ) { // 这个等号其实可以不用,这样子 pow2 也不需要开 LL 了
if (k >= pow2 - i - 1 && k <= pow2) res += C(i + 1, pow2 - k);
pow2 *= 2;
}
return res;
}
};