无法理解动态编程

时间:2016-03-04 10:37:27

标签: java dynamic-programming

我遇到了这个问题。给定一个三角形,找到从上到下的最小路径总和。您可以移动到下面一行中相邻数字的每一步。

 [
    [2],
   [3,4],
  [6,5,7],
 [4,1,8,3]
]

这是动态编程的一个例子。但是当我来练习时,这对我来说是一个非常困难或令人困惑的概念。我在线观看了视频和阅读教程,起初看起来很简单,但是当我遇到问题时,我完全迷失了。 所以我在网上找到了一个解决方案,它采用了一种底层方法:

public init minmumTotal(ArrayList<ArrayList<Integer>> triangle) {
       if (triangle.size() == 0 || triangle == null)
                   return 0;
     int[] dp = new int[triangle.size()+1]; // store each index’s total
       for (int i = triangle.size()-1; i >=0; i--) {
             for (int j = 0; j < triangle.get(i).size(); j++) {
                // first round: dp[j], dp[j+1] are both 0
               dp[j] = Math.min(dp[j], dp[j+1]) + triangle.get(i).get(j); 
             }
         }
             return dp[0];
         }

完成解决方案后似乎很容易。但这可以使用自上而下的方法来完成吗?有人可以解释为什么底层方法比自上而下方法更好?另外,从上到下或从下往上使用是否合适?此外,由于问题提到每个"Each step you may move to adjacent numbers on the row below."这意味着每一行是否在我进入下一行之前迭代整个列?

2 个答案:

答案 0 :(得分:2)

我不确定这个解决方案是否算作动态编程,但我认为它非常有效。

您可以从三角形的底部开始,然后通过在三角形中向上移动来折叠它。对于下一行中的每个数字,添加其下面两个数字的最小数字。当你到达顶部时,你将只有一个数字,这将是你的结果。所以你会得到这个:

开始:

   2
  3 4
 6 5 7
4 1 8 3

第1步:

   2
  3 4
 7 6 10

第2步:

   2
  9 10

第3步:

   11

答案 1 :(得分:0)

有点偏离主题但是如果你真的想以正确的方式处理NullPointerExceptions,那么该解决方案中的第一个if语句需要被转换。

所以我尝试了自上而下的方法,但有一些问题。

首先,就像marstran已经说过的那样,最后你有更多的数字,需要进行最低限度的搜索。

其次,自下而上的方法使用了一个额外的数组字段来确保它不会遇到IndexOutOfBound Exceptions。我自己并没有真正找到一个自上而下的好方法(自下而上的方法有一个优势,你总是知道你有两个数字可以看到(左边的孩子和右边的孩子),自上而下的方法很多节点没有右边或左边的父节点。所以还有一些额外的if语句。

public static int minimumTotal(ArrayList<ArrayList<Integer>> triangle) {
    if (triangle == null || triangle.isEmpty()) return 0;

    int[] results = new int[triangle.size()];   

    for (int i = 0; i < triangle.size(); i++) {
        ArrayList<Integer> line = triangle.get(i);

        for (int j = line.size() - 1; j >= 0; j--) {
            if (j == 0) results[j] = line.get(j) + results[j];
            else if (j >= i) results[j] = line.get(j) + results[j - 1];
            else results[j] = line.get(j) + Math.min(results[j], results[j - 1]);
        }
    }

    int minimum = results[0];
    for (int i = 1; i < results.length; i++) {
        if (results[i] < minimum) {
            minimum = results[i];
        }
    }

    return minimum;
}

无论如何,这与我自己采用自上而下的方法一样接近给定的解决方案。

请记住,没有人强迫您只使用1d数组作为结果。如果这个概念太难以提出,你可以简单地使用一个二维数组。它会增加你需要编写的代码量,但可能更容易想出来。