思路:
题目的主要信息:
- 跳上n级台阶,每次只能跳奇数级
- a数组中存放着不能跳上的台阶序号
- 达不到n级就返回0
- 求跳上n级的方案数,结果需要取模1e9+7
方法一:动态规划
具体做法:
我们可以用动态规划,辅助数组dp,其中表示i级台阶方法数,
有如下情况:
- 如果i级台阶有积水,
- 如果i为奇数,那它肯定由偶数级台阶跳来,于是
,对于每个偶数级台阶跳一个相差的奇数即可
- 如果i为偶数,那它肯定由奇数级台阶挑来,于是
,理由同上
按照上述说法,我们可以计算前缀奇数和与前缀偶数和来辅助dp数组。
class Solution {
public:
int solve(int n, int m, vector<int>& a) {
int mod = 1e9 + 7;
vector<int> dp(n + 1, 0); //dp[i]表示i级台阶方法数
dp[0] = 1;
int odd = 0; //奇数台阶方法种类前缀和
int even = 1; //偶数台阶方法种类前缀和
int index = 0; //记录数组a的下标
for(int i = 1; i <= n; i++){
if(i == a[index]){ //积水台阶不算
index++;
continue;
}
if(i % 2 == 1){ //奇数
dp[i] = (dp[i] + even) % mod;
odd = (odd + dp[i]) % mod;
}
else{ //偶数
dp[i] = (dp[i] + odd) % mod;
even = (even + dp[i]) % mod;
}
}
return dp[n];
}
};复杂度分析:
- 时间复杂度:
,遍历一次即可
- 空间复杂度:
,辅助数组dp的chang
方法二:递归
具体做法:
按照上述动态规划的思路,依靠前缀奇数和前缀偶数,对于某一个问题,我们可以自顶向下递归地求它的前缀奇数和或者偶数和,以2为遍历基数,将其累加。
class Solution {
public:
int mod = 1e9 + 7;
int recursion(int n, vector<bool>& water){
if(water[n]) //有积水
return 0;
if(n <= 1) //不大于1级的方案都是1
return 1;
int res = 0;
for(int i = n - 1; i >= 0; i = i - 2) //求其奇数前缀和或者偶数前缀和
res = (res + recursion(i, water)) % mod;
return res;
}
int solve(int n, int m, vector<int>& a) {
vector<bool> water(n + 1, false);
for(int i = 0; i < m; i++) //标记有水的台阶
water[a[i]] = true;
return recursion(n, water);
}
};复杂度分析:
- 时间复杂度:
,递归为
,每次累加前缀和也是
- 空间复杂度:
,递归栈深度及辅助数组water的长度

京公网安备 11010502036488号