正文
这一周都在搞这个dynamic programming,感觉确实是不光是看视频还是自己的琢磨,题看懂了听懂了,但是一下手就写着写着就停笔了。然后再看人家的答案,好的答案越看越明白,不好的答案越看越迷糊。总结一下,有时候答案可能翻一下才会恍然大悟!
以一道leetcode的题为列子说一下我的感受!
https://leetcode-cn.com/problems/triangle/description/ 120. 三角形最小路径和 这是一道典型的dp问题。一般解决dp问题都是从Fibonacci函数过来,因为过程会有很多重复问题,所以解决这种问题就成了动态规划问题。
这道题当然我是听过视频,但是没有明白,然后自己就去搞,搞的晕晕乎乎的,怎么看懂了,想明白了代码却写不出来,最后还是结合视频和官网解析突然发现了跟自己的代码不同的地方,那么来分析这个问题。
集合里面套集合,其实就是一个二维数组。每一行的元素到达下一行的路径是当前列 或者 当前列的下一列,按照这个思路遍历下去我们可以得到每一行的每一个元素的最短路径其实就是他的左右子的最短路径 加上 自己本身,就是
probleme(row,col) = min(sub(row+1,col) ,sub(row+1,col+1) +self(row,col)。
说明中有说空间复杂度O(n)就更好了,我一开始一直想这从上往下走,那么就定义一个数组是行数 int[] minPath = new int[row.length+1];然后就去写代码按照公式:f(row,col) = min(f(row+1,col),f(row+1,col+1) + self[row,col];
先把第一个元素放进数组中。
minPath[0] = triangl.get(0).get(0);
说实话写到了这里我突然对从上到下有了思路,因为一开始确实没做出来。然后我就去分析了一下,从上到下,计算每一个元素时候需要计算从根到这一点的路径值。要分三种情况:
1、如果当前是第一位元素,那么其实就是最左边,这个时候其实我们只需要在我们数组中拿到同一个索引下的值 再加上 本身的值就是从根到这个位置的路径值最佳的哦。
2、如果当前是最后一位,那么就是最右边,我们只需要考虑在我们的存最佳路径值数组的最后一位 再加上这个元素本身 这就是最右边的路径最小值
3、那么除了1 ,2情况,剩下的就是中间的情况了。中间情况就是需要我们拿到上一行同列和前一列的值然后对比拿到其中最小值 再加上当前元素那么就是最小路径值。
可能我说了这些有的同学不是很好理解,那么我自己画个图吧。原图 到 变化后的图。粉色是最左边的元素就是本身加上上一级同色元素,绿色是最右边的元素就是本身加上上一级同色的元素。中间蓝色的是通过上一级同一列位置和前一列职位元素对比后拿一个小的和当前元素相加。关键代码的部分还需要一个临时的数组用来作为中转计算作用,要不我们的最优值数组会被冲刷导致计算错误。
然后再奉上代码,希望这个从上往下说明白了。
public int minimumTotal(List<List<Integer>> triangle) {
//terminator
if (triangle == null || triangle.size() == 0) {
return 0;
}
//重复问题(分治) problme(i,j) = min(sub(i+1,j) ,sub(i+1,j+1)) + self[i,j]
//定义状态数组 f
int[] minPath = new int[triangle.size()];//只存每行的最最小路径
minPath[0] = triangle.get(0).get(0);
for (int i = 1; i < triangle.size(); i++) {
//need other arrays to store the data of prev
int[] temp = Arrays.copyOf(minPath, minPath.length);
for (int j = 0; j < triangle.get(i).size(); j++) {
//dp方程公式 f(i,j) = min ( f(i,j) ,f(i,j)) + self[i,j]
//当前行的每一位最佳路径都是有下一行的左右子算出来最小的一步一步往上走
if (j == 0) {
//最左边 那么直接就是上一行最优解的第一位 + 本身
minPath[j] = temp[j] + triangle.get(i).get(j);
} else if (j == triangle.get(i).size() - 1) {
//最右边的一个那么就是上一行的最后一位 + 本身
minPath[j] = temp[j - 1] + triangle.get(i).get(j);
} else {
//其他就是中间情况 就是 本列 + 上一行通一列和前一列最小值
minPath[j] = Math.min(temp[j], temp[j - 1]) + triangle.get(i).get(j);
}
}
}
int res = Integer.MAX_VALUE;
for (int min :
minPath) {
res = Math.min(res, min);
}
return res;
}
还有就是自底往上,相对来说要好一些。想一下,就是还用一个数组用来存放每行的最优结果值。从最底层往上走,那么走到顶层,那么数组的第一位就是那个要求的值了。
https://leetcode.com/problems/triangle/discuss/701936/Java-98-fast-1ms-6-lines-of-code 这是国际站高手的代码例子
总结
这周动态规划真的搞了三天了,就是题有时候会明白但是代码写不下去,有时候直接就是题看不明白。所以还是要不听练,不停的去思考,真的思路通了,我想代码不是问题了。