zoukankan      html  css  js  c++  java
  • 跳台阶问题分析

    问题描述:

             一个台阶,一次可以跳3级或者5级,跳到第n级有多少种跳法。

    问题分析:

             刚开始的思路是,每次跳3级或者5级,不一定能跳到第n级,要求n是3的倍数,或者是5的倍数,或者是3i和5j的和(i>=0,j>=0)。所以考虑三种情况:

    1、  n是3的倍数;

    2、  n是5的倍数;

    3、  3i+5j=n(i>=0且j>=0)。其他情况则是不可到达第n级。

    情况1和情况2很容易,直接求倍数就可以。情况3考虑的是两层for循环,使用蛮力法找到满足条件的i和j的值。后来发现,情况1和情况2不需要在算法中写具体实现,不满足情况1和情况2,不可到达第n级,则跳法为0。

      另一种是递归。递归要满足两个条件:1、存在相同的情景,可以重复调用;2、存在最终的结束条件。

      最终的结束条件很容易想到,那就是最后只剩下3级台阶或者5级台阶的情况,一次可以跳完,只有一种跳法。相同的情景可以这么考虑,开始跳之前,还剩下n级台阶没有跳过,跳过一次后,比如跳3级或者5级,则剩下n-3或者n-5,再开始第二次跳,发现第二次跳和第一次跳面临的选择是相同的,也是跳3级或者5级,只是要跳的台阶数减少了3级或者5级。那么每次的跳是相同的情景,可以重复调用。如果用函数jumpCount(n)计算跳法的话,n级台阶的跳法即是jumpCount(n),按照递归的思路,如果第一次跳3级的话,则剩下n-3,再跳这n-3级台阶,这n-3级台阶的跳法是jumpCount(n-3)。同理,如果第一次跳5级的话,则剩下n-5级的跳法是jumpCount(n-5);以此递推,每次都是在剩下的台阶数中跳3级或者5级,剩下n-3或者n-5,再重复跳,一直到最后只剩下3级或者5级。那么n级台阶的跳法是jumpCount(n)= jumpCount(n-3)+ jumpCount(n-5)。其实这里发现和第一种思路的第3中情况是类似的。

             这里当时有个思想误区,首先确实是考虑到了将剩下的台阶数作为下次跳跃的总数,每次跳跃进行递归的调用。但是受第一种思路的影响,每次都在考虑n-3i或者n-5j的情况,没有想到递归的核心思想是整体与局部的思想,问题分大的模块来看待,每个小模块再一步一步细分。每个大模块和小模块是相似的关系。每个小模块只需要考虑一步,不需要考虑整体情况,所以不需要考虑i和j了。

    问题验证:

    蛮力法代码:

     1     /**
     2      * 思路一:非递归,蛮力法找到满足条件的值
     3      * @param n 待跳的台阶数
     4      * @return 跳法
     5      */
     6     public static int jumpCount1(int n) {
     7         int count = 0;//跳法
     8         //i最大不能超过n的约数,同理j
     9         for (int i = 0; i <= n / 3; i++) {
    10             for (int j = 0; j <= n / 5; j++) {
    11                 if (3 * i + 5 * j == n) {
    12                     count++;//满足条件,则找到了一种跳法
    13                 }
    14             }
    15         }
    16 
    17         return count;
    18     }

    递归代码:

     1     /**
     2      * 思路二:递归
     3      * @param n 待跳的台阶数。每次跳3级或者5级
     4      * @return 跳法
     5      */
     6     public static int jumpCount(int n) {
     7         if (n < 3) {
     8             return 0;//如果小于3,则跳不了
     9         }
    10 
    11         if (n == 3 || n == 5) {
    12             return 1;//只剩下3级或者5级,则只有一种跳法,跳3级或者5级,一次跳完
    13         }
    14 
    15         return jumpCount(n - 3) + jumpCount(n - 5);//每次跳跃,剩下为n-3或者n-5,作为下次待跳跃的台阶数
    16     }

    蛮力法时间复杂度为O(n^2),递归为O(n),递归时间复杂度优于蛮力法。递归算法明显优于蛮力法,简介清晰,但是数据量大的情况下运行效率较低,内存开销大。

    总结:

    1、  蛮力法很容易想到,思想误区是要跳出判断是否能跳到第n级台阶的情况,这种情况跳法就是0,不需要代码中判断。

    2、  递归法要牢牢抓住递归的思想,考虑整体就不要管部分,考虑部分就不要管整体。

    更正

    经其他博友提示,蛮力法有问题。

    蛮力法只找到了跳完n级台阶,需要跳3级多少次,跳5级多少次,满足n级台阶则算一次。比如说8级台阶,需要跳3级台阶1次,5级台阶1次。但是实际上有两种跳法:1、先跳3级,再跳5级。2、先跳5级,再跳3级。有两种,但是按照蛮力法只有一次,蛮力法应该算组合。

    算得跳3级i次,跳5级j次,则总共需要跳i+j次,i+j次中,3级台阶有多少种可能,剩下的就是5级台阶,每种可能就是一种跳法(或者算5级台阶有多少种可能)。其实就是i+j中的i组合。

    Ci+ji

     1   public static int jumpCount1(int n) {
     2     int count = 0;// 跳法
     3     // i最大不能超过n的约数,同理j
     4     for (int i = 0; i <= n / 3; i++) {
     5       for (int j = 0; j <= n / 5; j++) {
     6         if (3 * i + 5 * j == n) {
     7           // count++;// 满足条件,则找到了一种跳法
     8           count = count + C(i, i + j);//i+j次跳跃中,跳3级台阶的可能次数,每次算一种跳法(找到了3的位置,则剩下为5的位置。或者找跳5级的位置也可以)
     9         }
    10       }
    11     }
    12 
    13     return count;
    14   }
    15 
    16   // 求排列数
    17   private static int A(int up, int bellow) {
    18     int result = 1;
    19     for (int i = up; i > 0; i--) {
    20       result *= bellow;
    21       bellow--;
    22     }
    23     return result;
    24   }
    25 
    26   // 求组合数
    27   private static int C(int up, int below) {
    28     // 分母
    29     int denominator = A(up, up);// A(6,6)就是求6*5*4*3*2*1,也就是求6的阶乘
    30     // 分子
    31     int numerator = A(up, below);// 分子的排列数
    32 
    33     return numerator / denominator;
    34   }

    感谢博友的指正!

  • 相关阅读:
    Effective Java 第三版——26. 不要使用原始类型
    Effective Java 第三版——25. 将源文件限制为单个顶级类
    Effective Java 第三版——24. 优先考虑静态成员类
    Effective Java 第三版——23. 优先使用类层次而不是标签类
    Effective Java 第三版——22. 接口仅用来定义类型
    Effective Java 第三版——21. 为后代设计接口
    Effective Java 第三版——20. 接口优于抽象类
    Effective Java 第三版——19. 如果使用继承则设计,并文档说明,否则不该使用
    Effective Java 第三版——18. 组合优于继承
    Effective Java 第三版——17. 最小化可变性
  • 原文地址:https://www.cnblogs.com/leanfish/p/7001258.html
Copyright © 2011-2022 走看看