一、题目描述

JZ9跳台阶扩展问题
本题有一个简单版本,建议先做一下题目链接
题目大意:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
注意审题:每一种跳法都是顺序相关的,即先跳一步再跳两步不等价于先跳两步再跳一步

二、算法1(递归)

算法思路

1.解题思路:暴力尝试每一种走法,到达终点后记为一种方案
2.实现方式:递归时我们需要知道当前处于第几级台阶,以及目标是到达第几级台阶,因此递归函数接受两个参数,一个是当前所在的位置,一个是目标位置,递归边界即当前所在位置为目标位置时结束,每个阶段尝试不同的步数即可.
3.这种解法的时间复杂度过高,因此不建议采用

代码实现(C++11)

class Solution {
public:
    int jumpFloorII(int number) {
        return dfs(0, number);
    }

    int dfs(int cur, int target){
        if(cur == target) return 1;
        int tot = 0;
        for(int i = 1; cur + i <= target; i++)
            tot += dfs(cur + i, target);
        return tot;
    }
};

时间复杂度:O(n^n)
空间复杂度:O(n^n)

时间复杂度优化(记忆化搜索)

1.对于递归求方案数的问题,我们可以自然的联想到用记忆化搜索来进行时间优化,因为在递归的过程中可能会出现大量相同的分支,所以我们可以使用一个备忘录数组记录已知的分支,当遇到重复子问题时,直接返回备忘录记下的结果即可
2.根据递归的参数,我们可以想到一种定义备忘录数组的方式,即memo[i]表示从第i级台阶走到终点的方案数
3.实现方式:一般来说,我们习惯将memo数组初始化为-1,表示还未求得方案,因为对于一些问题来说0也是一种合法方案

代码实现(C++11)

class Solution {
public:
    vector<int> memo;

    int jumpFloorII(int number) {
        memo.resize(number + 1, -1);
        return dfs(0, number);
    }

    int dfs(int cur, int target){
        if(memo[cur] != -1) return memo[cur];
        if(cur == target) return 1;
        int s = 0;
        for(int i = 1; cur + i <= target; i++){
            s += dfs(cur + i, target);
            }
        return memo[cur] = s;
    }
};

时间复杂度:平均时间复杂度O(n^2)
空间复杂度:O(n^2)

三、算法2(动态规划)

算法思路

1.解题思路:与跳台阶问题相似,定义f数组,f[i]表示从起点到达第i级台阶的方案数,由于每次跳的步数无限制,因此可以求得状态转移方程:
图片说明

代码实现(C++11)

class Solution {
public:
    int jumpFloorII(int number) {
        int f[number + 1];
        f[0] = 1;
        f[1] = 1;
        for(int i = 2; i <= number; i++){
            f[i] = 0;
            for(int j = 0; j < i; j++){
                f[i] += f[j];
            }
        }
        return f[number];
    }
};

时间复杂度:O(n^2)
空间复杂度:O(n)

时间复杂度优化(数学)

观察状态转移方程,我们可以看出F(x - 1) = F(0) + F(1) + ... + F(x - 2),这与F(x)的方程有了重复的部分,替换可得F(x) = 2 * F(x - 1) (x > 1)
F(0) = 1
F(1) = 1
F(2) = 2
F(3) = 4
...
F(n) = 2 * F(n - 1)
可见从F(1)开始满足F(i) = 2^(i - 1)
故可以可以快速幂算法将时间复杂度优化至O(logn)级别

代码实现(C++11)

class Solution {
public:
    int jumpFloorII(int number) {
        if(number == 0) return 1;
        return qmi(2, number - 1);
    }

    int qmi(int a, int b){
        int res = 1;
        while(b){
            if(b & 1) res *= a;
            a *= a;
            b >>= 1;
            }
        return res;
    }
};

时间复杂度:O(logn)
空间复杂度:O(1)