zoukankan      html  css  js  c++  java
  • 跳青蛙问题与变态跳青蛙问题

    今天在网上看到两个算法题,分别是跳青蛙问题与变态跳青蛙问题,具体题目如下。

    问题

    跳青蛙问题:

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

    这道题看似简单,但是我看了半天也没想出个突破口,在本子上画来画去也没想到解决的方法。

    直到我将台阶的个数于跳法的个数罗列出来才发现端倪:

    台阶数            跳法数
    1                    1
    2                    2
    3                    3
    4                    5
    5                    8
    ...                   ...

    这尼马不就是斐波那彻数列么?

    这下问题转变成了,如何求斐波那彻数列?

    方法分两种:递归和非递归。

    其实递归的方法是更加容易理解的,写起来也是最简单的。我们直到斐波那彻数列的公式:

    f(n) = f(n-1) + f(n-2), n >= 2

    套用公式就能写出递归的方法:

    int func(int number) {
        if (number <= 0) {
            return (-1);
        }
        else if (1 == number){
            return 1;
        }
        else if (2 == number) {
            return 2;
        }
        else {
            return func(number - 1) + func(number - 2);
        }
    }

    关于递归方法时间复杂度,这里就不做研究了,有兴趣的朋友可以看看这篇博客,讲的非常好,因为我看不懂:

    http://www.cnblogs.com/python27/archive/2011/12/09/2282486.html

    其实递归方法的效率是非常慢的,如果在面试的时候,明明可以用非递归的方法写出答案,你偏偏要用递归,那你就是做死了(真事儿,我朋友面别人就用这标准)。

    下面介绍非递归的方法,先来看看代码:

    int func(int number) {
        if(number<=3)
            return number; 
        int f2=1; 
        int f1=0; 
        int f=0; 
        for(int i=0;i<number;++i) { 
            f=f1+f2;
            f1=f2;
            f2=f;
        } 
        return f;    
    }

    很明显,时间复杂度为O(n),比递归的方法快了很多倍。

    这种方法巧妙的利用了求解公式,类似于用了两个指针,分别指向了被求数的前两位数,得到被求数后,再将其值赋予其中一个指针,另一个指针则往前挪。

    看过了正常的跳青蛙问题,我们再来看变态的跳青蛙。

    具体题目如下:

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

    套用吴宗宪大哥的话说就是:哎哟,这个就厉害了。

    这次青蛙可以任意的选择跳上的台阶数,所以不再是一个标准的斐波那契数列问题,不过没关系,我们先找寻问题的规律,再从规律寻找突破口。

    根据题目,有:

    f(0) = 1

    f(1) = 1

    f(2) = f(2-1) + f(2-2)

    f(3) = f(3-1) + f(3-2) + f(3-3)

    ...

    f(n) = f(n-1) + f(n-2) + ... + f(n-(n-1)) + f(n-n)

    简单的解释一下:例如f(3-1)表示3阶跳了1阶后,剩下的跳阶方法数,f(3-2)表示3阶跳了两阶后剩下的跳阶方法数,以此类推,直到一次跳n阶后,剩下的跳阶方法数。

    现在问题明了了很多,但是还不够,我们可以继续对其进行分解:

    因为 : f(n) = f(n-1) + f(n-2) + ... + f(n-(n-1)) + f(n-n) = f(0) + f(1) + f(2) + ... + f(n-2) + f(n-1)

    所以 : f(n-1) = f(0) + f(1) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + ... + f(n-2)

    则: f(n) = f(n-1) + f(n-1) = 2*f(n-1)

    现在可以根据这个式子来写出代码了。

    同样,我们先写出容易写的递归方法:

        int func(int number) {
            if (number <= 0) {
                return -1;
            } else if (number == 1) {
                return 1;
            } else {
                return 2 * func(number - 1);
            }
        }

    然后是非递归的方法:

    int func(int number) {
    
        if (number <= 0) {
            return (-1);
        }
    
        if (1 == number) {
            return 1;
        }
    
        int f1 = 1;
        int f = 0;
        for (int i = 0; i < number - 1; i++) {
            f = 2 * f1;
            f1 = f;
        }
    
        return f;
    }

    总结

    通过对这两个问题的分析和解决,我深刻的明白了一个道理:解决问题之前,我们必须充分的了解问题,把握问题的规律。科学家们一直致力于寻找物质的规律,世界的规律以及宇宙的规律。而算法的美妙之处就在于,你通过不断的训练,能从纷杂的问题表面分析出实质性的规律,就如同在茫茫互联网海洋中,寻找那一段段神秘的代码。

    今天的博客就写道这里了,老司机催我上车了。

  • 相关阅读:
    算法问题实战策略 JUMPGAME 记忆化搜索
    算法问题实战策略 TRIANGLEPATH 动态规划入门题
    poj 2785 4 Values whose Sum is 0
    poj 3276 Face The Right Way 递推
    acwing 883. 高斯消元解线性方程组
    acwing 861. 二分图的最大匹配 模板
    Leetcode 42 接雨水 双指针 空间换时间
    LeetCode 1290. 二进制链表转整数
    LeetCode 1291. 顺次数
    <挑战程序设计竞赛> poj 3320 Jessica's Reading Problem 双指针
  • 原文地址:https://www.cnblogs.com/xyliao/p/5263301.html
Copyright © 2011-2022 走看看