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   }

    感谢博友的指正!

  • 相关阅读:
    fastjson-alibaba
    ubuntu 入门
    资料文档
    asp.net mvc View视图相关
    视频
    js第三方
    工具类网址
    [转]初学者的编程自学指南
    seajs的使用--主要了解模块化
    其它
  • 原文地址:https://www.cnblogs.com/leanfish/p/7001258.html
Copyright © 2011-2022 走看看