zoukankan      html  css  js  c++  java
  • 猴子爬山问题-递归

    一个顽猴在一座有k级台阶的山上爬山跳跃,猴子上山一步可跳1级或跳3级,试求顽猴上山的N级台阶有多少种不同的跳法;

    1.递推设计:

    这一问题实际上是一个整数有序可重复拆分问题;

    试设置数组应用递推求解,设上k级台阶的不同跳法为f(k)种;

    (1)、探求f(k)的递推关系;

    假设:k=100;

    上山最后一步到达第100级台阶,完成上山,共有f(100)种不同的爬法,到第100级之前位于哪一级呢?无非是位于第99级(上跳1级即到),有f(99)种;

    或位于第97级(上跳3级即到),有f(97)种,于是:

    • f(100)=f(99)+f(97)
    • f(99)= f(98)+f(96)
    • f(97)= f(96)+f(94)
    • 依次类推

    以此类推,一般地有递推关系:

    • f(k)=f(k-1)+f(k-3) (k>3)

    (2)、确定初始条件:

    • f(1)=1,即1=1;

    • f(2)=1,即2=1+1(注意:跳法中不允许直接跳2级);

    • f(3)=2,即3=1+1+1,3=3;

    (3)、实施递推;

    根据以上递推关系与初始条件设置一重k(4~n)循环,循环外确定初始条件,循环内实施递推:

    • f[k]=f[k-1]+f[k-3],即可求出f(n);//与斐波那契数列的求解一样

    此具体案例的递推设计比较简单,时间复杂度为O(n);

    程序设计(Java):

     public static void main(String[] args){
            Scanner sc = new Scanner(System.in);
            int k = sc.nextInt();
            System.out.println(recursion(k));
        }
    
        static int recursion(int k){
            //f(1)=1,即1=1;
            //f(2)=1,即2=1+1(注意:跳法中不允许直接跳2级);
            //f(3)=2,即3=1+1+1,3=3;
            if(k==1 || k==2){
                return 1;
            }
            if(k==3){
                return 2;
            }
            return recursion(k-1)+recursion(k-3);
        }

    2.一般情形的分级递推:

    把问题引申为爬山n级台阶,一步有m种跨法,具体一种跨法跳多少级均从键盘输入;

    (1)、分级递推设计;

    1)、设置两个数组;

    爬山t级台阶的不同爬法为f(t),从键盘输入一步跨多少级的m个整数为x[i](i=1,2,……,m);

    这里的整数x(1),x(2),……,x(m)(约定x(1)< x(2)<……< x(m)< n)为键盘输入,事前并不知道,因此不能在设计时简单地确定初始值f(x(1)),f(x(2)),……;

    事实上,可以把初始条件放在分级递推中求取,应用多关系分级递推算法完成递推;

    2)、确定f(t)的递推关系;

    当t< x(1)时,f(t)=0,f(x(1))=1 (初始条件);

    当x(1)< t<=x(2)时,第1级递推:f(t)=f(t-x(1));

    当x(2)< t<=x(3)时,第2级递推:f(t)=f(t-x(1))+f(t-x(2));

    ===================================

    一般的,当x(k)< t<=x(k-1),k=1,2,……,m-1,有第k级递推:

    f(t)=f(t-x(1))+f(t-x(2))+……f(t-x(k));
    当x(m)< t时,第m级递推:

    f(t)=f(t-x(1))+f(t-x(2))+……+f(t-x(m));
    当t=x(2),或t=x(3),……,或t=x(m)时,按上面递推求f(t)外,还要加上1,道理很简单,因为此时t本身即为一个一步到位的爬法,为此,应在以上递推基础上添加:

    f(t)=f(t)+1 (t=x(2),x(3),……,x(m));

    ======================
    所求的目标为:

    f(n)=f(n-x(1))+f(n-x(2))+……+f(n-x(m));

    ==========================================

    这一递推式是我们设计的依据;

    3)、设x(m+1)的技巧;

    在递推设计中可以把台阶数n记为数组元素x(m+1),这样处理是巧妙地,可以按相同的递推规律递推计算,简化算法设计,最后一项f(x(m+1))即为所求f(n);

    最后输出f(n)即f(x(m+1))时必须把额外所添加的1减去;

    注意:上述求解过程中存在重复计算某个值,可以画出递归树来看,可以使用备忘录或者table表去优化。

    可参考:https://www.cnblogs.com/controller666/p/14520459.html   带备忘录的递归解法

    优化代码:

      public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int k = sc.nextInt();
            System.out.println(recursion(k));
        }
    
        public static int recursion(int n) {
            int[] res = new int[n+1];
            //备忘录全初始化为-1
            for (int i = 0; i < n+1; i++) {
                res[i] = -1;
            }
            //等于0的情况返回就是0
            if (n == 0) {
                res[n] = 0;
                return 0;
            }
            //等于1或者2的情况就是1 并记录已计算过
            if (n == 1) {
                res[n] = 1;
                return 1;
            }
            if (n == 2) {
                res[2] = 1;
                return 1;
            }
            //等于3的情况就是2 并记录已计算过
            if (n == 3) {
                res[3] = 2;
                return 2;
            }
            //如果数组中的值不是-1,则说明计算过,则直接返回,不再重复计算
            if (res[n] != -1)
                return res[n];
            res[n] = recursion(n - 1) + recursion(n - 3);
            return res[n];
        }
    不忘初心,相信自己,坚持下去,付诸实施。
  • 相关阅读:
    并发编程(十)—— Java 并发队列 BlockingQueue 实现之 SynchronousQueue源码分析
    并发编程(九)—— Java 并发队列 BlockingQueue 实现之 LinkedBlockingQueue 源码分析
    Java工程师成神之路
    并发编程(八)—— Java 并发队列 BlockingQueue 实现之 ArrayBlockingQueue 源码分析
    星空雅梦
    星空雅梦
    星空雅梦
    星空雅梦
    星空雅梦
    星空雅梦
  • 原文地址:https://www.cnblogs.com/controller666/p/14532091.html
Copyright © 2011-2022 走看看