N级台阶走法(算法问题)

N级台阶走法

题目: 总共100级台阶(任意N级都行),小明每次可选择走1步、2步,问走完这100级台阶总共有多少种走法?

分析:对于台阶走法

假设只有一个台阶,那么只有一种跳法,那就是一次跳一级,f(1)=1;如果有两个台阶,那么有两种跳法,第一种跳法是一次跳一级,第二种跳法是一次跳两级, 如果有大于2级的n级台阶,那么假如第一次跳一级台阶,剩下还有n-1级台阶,有f(n-1)种跳法,假如第一次条2级台阶,剩下n-2级台阶,有f(n-2)种跳法。

​ 只有1阶时,有F(1) -> 1;

​ 2阶时,有F(2) -> 2 种走法;

​ 3阶时,有F(3) = F(1) + F(2) 种走法;

​ ```

​ N阶时,有F(N) = F(N-1) + F(N-2) 种走法;

可知为斐波那契数列。对于代码实现,可以通过递归思想来实现;

//走台阶的基础走法oneStep_lv0()方法
private static long oneStep_lv0(long stpes){
        if(stpes < 1){
            return 0;
        }
        if(stpes == 1){
            return 1;
        }
        if(stpes == 2){
            return 2;
        }
        return oneStep_lv0(stpes-1)+oneStep_lv0(stpes-2);
}
//调用 oneStep_lv0()方法
 private static void doAnswer(int stpes){
		System.out.print(oneStep_lv0(stpes));
 }

分析:此算法简单粗暴,通过递归调用解决走台阶问题,但分析我们会发现 当F(100)时会计算F(99) 和F(98),而计算F(99)时 又需要计算F(98)、F(97) ,F(98) 就会重复计算,实际执行代码时我们会发现,计算的耗时会非常之久,为了避免这个问题,我们可以用空间换时间:

既然计算F(100) 必须要 计算 F(98)+F(99),那么意味着,我其实计算F(N)时,需要将之前的 N-1种情况都计算了,那么我们可以由低阶往高阶计算,并将每次结果存储起来,优化代码如下:

 private static void doAnswer(int stpes){
     //定义数组,存放每次走台阶对应的步数
        long[] answerArr = new long[stpes+1];
        for (int i = 1; i <= stpes ; i++) {
            //从第1阶开始,循环调用
            answerArr[i] = oneStep_lv1(i,answerArr);
        }
        System.out.print(answerArr[stpes]);
    }

//走台阶的基础走法oneStep_lv1()方法
private static long oneStep_lv1(int stpes,long[] answerArr){
        if(answerArr[stpes] != 0){
            //台阶之前走过,则直接返回上次计算结果
            return answerArr[stpes];
        }
        if(stpes == 1){
            return 1;
        }
        if(stpes == 2){
            return 2;
        }
        return oneStep(stpes-1,answerArr) + oneStep(stpes-2,answerArr);
    }

改进后计算时间将大大减少。

注意由于100阶的结果是 573147844013817084101 会溢出 long 的最大长度,所以需要将结果换为BigDecimal,在此省略改造代码。

衍生:

第二种解法其实用到了动态规范的相关思想:

动态规划原理

  • 基本思想:问题的最优解如果可以由子问题的最优解推导得到,则可以先求解子问题的最优解,在构造原问题的最优解;若子问题有较多的重复出现,则可以自底向上从最终子问题向原问题逐步求解。
  • 使用条件:可分为多个相关子问题,子问题的解被重复使用
    • Optimal substructure(优化子结构):
      • 一个问题的优化解包含了子问题的优化解
      • 缩小子问题集合,只需那些优化问题中包含的子问题,降低实现复杂性
      • 我们可以自下而上的
    • Subteties(重叠子问题):在问题的求解过程中,很多子问题的解将被多次使用。
  • 动态规划算法的设计步骤:
    • 分析优化解的结构
    • 递归地定义最优解的代价
    • 自底向上地计算优化解的代价保存之,并获取构造最优解的信息
    • 根据构造最优解的信息构造优化解
  • 动态规划特点:
    • 把原始问题划分成一系列子问题;
    • 求解每个子问题仅一次,并将其结果保存在一个表中,以后用到时直接存取,不重复计算,节省计算时间
    • 自底向上地计算。
    • 整体问题最优解取决于子问题的最优解(状态转移方程)(将子问题称为状态,最终状态的求解归结为其他状态的求解)

通俗点,动态规划是把一个大问题拆解成一堆小问题,这个本身没啥问题,但是这个不是动态规划的核心思想,或者说,一个”大问题“之所以能用”动态规划“解决,并不是因为它能拆解成一堆小问题,因为任何大问题都能拆解成小问题.

取决于该问题是否能用动态规划解决的是这些”小问题“会不会被被重复调用

你可能感兴趣的:(算法之路,算法,动态规划)