题目来源:牛客网-剑指Offer专题题目地址:变态跳台阶

题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

题目解析一:直接推导

本质上是斐波那契数列的变种,普通跳台阶是一步与两步,问题规模缩小到分成最后要跳到第 n 阶可以跳两次或者一次去求解,所以,在普通跳台阶,设置两个临时变量存下跳一次或者两次时,前面会有多少种可能的结果
dp 就是可以由什么状态推导出最后的状态,斐波那契数列是由前两种状态,而这里就是由前 n - 1 种状态推导出
这里用同一个套路来分析一下

  • 若楼梯阶级 n = 3求得,n = 3 时,有 4 种跳法
    • 跳 3 步到 3:没有剩下步数没跳的,只有这样一种跳法
    • 跳 2 步到 3:剩下的是第一步没跳,起始跳到第一步只有一种
    • 跳 1 步到 3:剩下的是第二步没跳,起始跳到第二步有两种
  • 若楼梯阶级 n = 4求得,n = 4 时,有 8 种跳法
    • 跳 4 步到 4:没有剩下步数没跳的,只有这样一种跳法
    • 跳 3 步到 4:剩下的是第一步没跳,起始跳到第一步只有一种
    • 跳 2 步到 4:剩下的是第二步没跳,起始跳到第二步只有两种
    • 跳 1 步到 4:剩下的是第三步没跳,起始跳到第三步有四种
  • 若楼梯阶级 n = n
    • 跳 x 步到 n 有几种的和,跟前 n - 1 种状态有关

那么,设置一个数组即可,在求的过程中把值都暂时放在数组里,最后求的时候遍历数据这些求好的对应的阶级种数之和即为新的下级阶梯种数

  1. import java.util.*;
  2. public class Solution {
  3. public int JumpFloorII(int target) {
  4. if (target <= 2) {
  5. return target;
  6. }
  7. int[] dp = new int[target + 1];
  8. Arrays.fill(dp, 1); //初始化每一种都可以直接从 0 跳到 n
  9. dp[0] = 0; //从 0 跳到 0 为 0 种,因为 n = 0,没法跳
  10. for (int i = 2; i <= target; i++) {
  11. for (int j = i - 1; j >= 1; j--) {
  12. dp[i] += dp[j]; //第 n 个状态是由前 n - 1 种状态推导出来,就是累加!
  13. }
  14. }
  15. return dp[target];
  16. }
  17. }

题目解析二:公式推导

这道题有两种比较可靠的方法可以得到规律:
第一种就是观察法,我们先枚举出自己有耐心算得出来的部分,一般是如下表所示:image.png
基于我们强大的数学基础(良好的运气)和细致的观察能力(搏一搏的心态),我们可以发现规律就是剑指Offer JZ09 变态跳台阶(数列推导) - 图2
第二种是数学推导法,咋们当然还是信奉科学(玄学)的方法。
设该青蛙跳上一个剑指Offer JZ09 变态跳台阶(数列推导) - 图3级的台阶总共有剑指Offer JZ09 变态跳台阶(数列推导) - 图4种跳法,则由题意可得
剑指Offer JZ09 变态跳台阶(数列推导) - 图5剑指Offer JZ09 变态跳台阶(数列推导) - 图6剑指Offer JZ09 变态跳台阶(数列推导) - 图7剑指Offer JZ09 变态跳台阶(数列推导) - 图8剑指Offer JZ09 变态跳台阶(数列推导) - 图9剑指Offer JZ09 变态跳台阶(数列推导) - 图10剑指Offer JZ09 变态跳台阶(数列推导) - 图11
由后面剑指Offer JZ09 变态跳台阶(数列推导) - 图12得,
剑指Offer JZ09 变态跳台阶(数列推导) - 图13剑指Offer JZ09 变态跳台阶(数列推导) - 图14是首项为剑指Offer JZ09 变态跳台阶(数列推导) - 图15,公比为剑指Offer JZ09 变态跳台阶(数列推导) - 图16的等比数列,即剑指Offer JZ09 变态跳台阶(数列推导) - 图17
公式都有了,解决起来就简单了,这里给大家提供三种实现方式。

方法一:快速幂

直接上快速幂的模板,剑指Offer JZ09 变态跳台阶(数列推导) - 图18的时间复杂度解决问题,妥妥的。

  1. public class Solution {
  2. private int quickPow(int a, int n) {
  3. int ans = 1;
  4. while (n != 0) {
  5. if (n % 2 == 1) {
  6. ans *= a;
  7. }
  8. a *= a;
  9. n /= 2;
  10. }
  11. return ans;
  12. }
  13. public int JumpFloorII(int target) {
  14. return quickPow(2, target - 1);
  15. }
  16. }

想学快速幂的小伙伴可以看这篇博客:数论基础之快速幂(详细教程)

方法二:求幂方法

调用Math类中的求幂方法pow,再进行强制类型装换,同样可以得到正确的结果。

  1. public class Solution {
  2. public int jumpFloorII(int target) {
  3. return (int)Math.pow(2,target-1);
  4. }
  5. }

方法三:利用位运算实现

不熟位运算的小伙伴可以看这篇博客:C++描述的位运算总结

  1. public class Solution {
  2. public int jumpFloorII(int target) {
  3. return 1<<(target-1);
  4. }
  5. }