【问题标题】:Java Programming : Dynamic Programming on stairs exampleJava 编程:楼梯上的动态编程示例
【发布时间】:2013-07-20 16:47:13
【问题描述】:

一个人正在跑上 n 级楼梯,一次可以走 1 级、2 级或 3 级。现在编写一个程序来计算孩子可以用多少种可能的方式跑楼梯。

给出的代码如下所示

public static int countDP(int n, int[] map) {
 if (n<0)
   return 0;
 else if (n==0)
   return 1;
 else if (map[n]>-1)
   return map[n];
 else {
    map[n] = countDP(n-1, map) + countDP(n-2, map) + countDP(n-3, map);
    return map[n]; }
 }

我知道 C 和 C++,而不是 JAVA。 这是来自Cracking the Coding 采访书。 谁能解释一下

  1. 她为什么以及如何在这里使用函数映射?这里的map是数组吧?

  2. 我没有看到任何将输入保存到地图数组的行,但它如何返回?

  3. 有人知道这段代码的 C++ 或 C 版本吗?很难理解这段代码。可能不是因为JAVA语法,而是动态规划的隐式结构。

  4. 这个算法的时间复杂度是多少?它应该小于 O(3^n) ?

我将不胜感激。

谢谢各位

【问题讨论】:

  • 我会尽力解决(神秘的)问题:1. map 是一个int 数组。 2. 它必须是在外部定义的,即不在这个例子中,并且必须包含 n+1 个元素 3. 不,如果你想回答这个问题,你必须将 CC++ 添加到标签 4. ...

标签: java algorithm recursion time-complexity dynamic-programming


【解决方案1】:

她为什么以及如何在这里使用功能图?

本书展示了一种名为memoization 的动态编程技术。它用于避免再次计算相同的数字:如果元素不是-1,则它已被再次计算,重新计算将意味着浪费大量的CPU周期。 DP 计算一次该值,然后在每次需要该值时返回它。

这里的map是数组吧?

正确,map 是数组类型。

我没有看到任何将输入保存到地图数组的行,但它会如何返回?

那就是倒数第三行的赋值:

map[n] = countDP(n-1, map) + countDP(n-2, map) + countDP(n-3, map);

有人知道这段代码的 C++ 或 C 版本吗?很难理解这段代码。可能不是因为JAVA语法,而是动态规划的隐式结构。

没错,DP 和 memoization 需要一些时间来适应。用纸和铅笔对一个小数字(例如 10)运行一次该算法。这将向您展示答案的最佳子结构如何帮助该算法如此快速地得出答案。

这个算法的时间复杂度是多少?它应该小于 O(3^n) ?

绝对!每个项目只计算一次,每个项目都需要摊销O(1)来计算,所以这段代码的整体复杂度是O(N)。这可能违反直觉,因为您观察到计算countDP(K) 的递归调用链如何采用O(K) 递归调用。但是,每次调用都会完成对 mapK 项的计算(注意 map 是一条单向街道:一旦您在单元格中设置了非负值,它将永远保持非负,因此通过任何其他调用路径重新计算相同的值将花费相同的 O(1) 时间。

【讨论】:

    【解决方案2】:

    1.) map 是一个整数数组。 Java 中的符号是 map[n] 返回索引 n 处的整数值。

    2.) 返回是一个整数,因为 map[n] 返回索引 n 处的整数值。值保存到数组的唯一时间是在

    map[n] = countDP(n-1, map) + countDP(n-2, map) + countDP(n-3, map);
    

    这是一个递归调用,通过计算所有可能的 1 、 2 和 3 组合来查找步数之和。

    3.)

    int countDP(int n, int map[])
    {
    if (n<0)
        return 0;
    else if (n==0)
        return 1;
    else if (map[n]>-1)
        return map[n];
    else {
        map[n] = countDP(n-1, map) + countDP(n-2, map) + countDP(n-3, map);
        return map[n]; 
    }
    
    
    }
    

    4.) 是的,复杂度会比 O(3^n) 快得多。

    【讨论】:

      【解决方案3】:

      好的,这就是代码的作用。

       `if (n<0)`
          `return 0;`
      

      如果剩余的步数不足,则不要计算。例如,如果还剩两步,但用户尝试走三步,则不能算作可能的组合。

      else if (n==0) return 1;

      如果剩余步数与用户尝试采取的可用步数相匹配,则可能是组合。因此,返回 1,因为这是一个可能的组合,应该添加到有效组合的总数中。

      else if (map[n]&gt;-1) return map[n];

      这里是动态规划部分。假设数组中的所有值的值为 -1。因此,如果数字大于 -1,则它已经被求解,因此返回第 n 步的组合总数而不是求解它。

      `map[n] = countDP(n-1, map) + countDP(n-2, map) + countDP(n-3, map);`
      

      return map[n]; }

      最后,这部分解决了代码。可能组合的数量等于用户走 1 步可获得的可能组合数量 + 用户走 2 步可获得的可能组合数量 + 用户走 2 步可获得的可能组合数量三个步骤。

      举个例子,假设有 5 个步骤

      简单的运行如下所示:

      //The number of solutions from the fifth step
      
      countDp(5) = countDp(4)+countDp(3)+countDp(2);
      
      //Number of solutions from the fourth step
      
      countDP(4) = countDp(3)+countDp(2)+countDp(1);
      
      //Number of solutions from the third step
      
      countDp(3) = countDp(2)+countDp(1)+countDp(0);
      //Number of solutions from the second step
      countDp(2) = countDp(1)+countDp(0)+countDp(-1);
      //Number of solutions from the first step
      countDp(1) = countDp(0) + countDp(-1)+countDp(-2);
      //Finally, base case
      countDp(0) = 1;
      
      countDp(-1)= 0;
      countDp(-2)= 0;
      countDp(1) = 1+0+0 = 1;
      countDp(2) = 1+1+0 = 2;  //Dynamic programming: did not have to resolve for countDp(1), instead looked up the value in map[1]
      countDp(3) = 2+1+1 = 4;  //Dynamic programming, did not have to solve for countDp(1), countDp(2), instead looked up value in map[1] and map[2]
      countDp(4) = 4+2+1=7 //Dynamic programming, did not have to solve for CountDp(3),CountDp(2), CountDp(1), just looked them up in map[3],map[2],map[1]
      countDp(5)=  2+4+7=13 //Dynamic programming, just used map[4]+map[3]+map[2]
      

      【讨论】:

        【解决方案4】:

        JavaScript 解决方案:(迭代)

        function countPossibleWaysIterative(n) {
          if (n < 0){
            return -1; // check for negative, also might want to check if n is an integer
          } if (n === 0) {
            return 0; // for case with 0 stairs
          } else if (n === 1) {
            return 1; // for case with 1 stairs
          } else if (n === 2) {
            return 2; // for case with 2 stairs
          } else {
        
            var prev_prev = 1;
            var prev = 2;
            var res = 4; // for case with 3 stairs
        
            while (n > 3) { // all other cases
              var tmp = prev_prev + prev + res;
              prev_prev = prev;
              prev = res;
              res = tmp;
              n--;
            }
          }
          return res;
        }
        

        【讨论】:

          【解决方案5】:
          /**
           * Created by mona on 3/3/16.
           */
          import java.util.Hashtable;
          public class StairCount {
              /*
               A man is running up a staircase with n steps, and can go either 1 steps, 2 steps,
                or 3 steps at a time. count how many possible ways the child can run the stairs.
               */
              static Hashtable<Integer, Long> ht=new Hashtable<>();
          
              public static long stairs(int n){
                  if (!ht.containsKey(1)){
                      ht.put(1, (long) 1);
                  }
                  if (!ht.containsKey(2)){
                      ht.put(2, (long) 2);
                  }
                  if (!ht.containsKey(3)){
                      ht.put(3, (long) 4);
                  }
          
          /*
                  if (!ht.containsKey(n)){
                      ht.put(n, stairs(n-1)+ht.get(1)+stairs(n-2)+ht.get(2)+stairs(n-3)+ht.get(3));
                  }
          */
                  if (!ht.containsKey(n)){
                      ht.put(n, stairs(n-1)+stairs(n-2)+stairs(n-3));
                  }
                  return ht.get(n);
              }
              public static void main(String[] args){
                  System.out.println(stairs(4));
          
              }
          }
          

          //4 的答案是 14,5 的答案是 27。对于被注释的行。有人可以评论为什么我的思维过程是错误的吗?

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-04-08
            • 1970-01-01
            • 1970-01-01
            • 2011-04-26
            • 2015-02-22
            • 1970-01-01
            • 1970-01-01
            • 2020-04-01
            相关资源
            最近更新 更多