Minimum Falling Path Sum


题目链接:题面

这题,我的思路是,类似于八皇后那样,用回溯的方法解决,想了好久的递归函数(我实在是太菜了),如下:

 1 private int dfs(int row, int col, int[][] a) {
 2         int n = a.length;
 3         if (row >= 0 && row < n && col >= 0 && col < n) {
 4             int left = dfs(row+1, col-1, a) + a[row][col];
 5             int mid = dfs(row+1, col, a) + a[row][col];
 6             int right = dfs(row+1, col+1, a) + a[row][col];
 7             return getMin(left, mid, right);
 8         } else if (row >= n) {
 9             return 0;
10         } 
11         return 105;
12 }

这个dfs函数的功能呢,就是我每遍历到一个矩阵中的数,然后判断这个位置合不合理,如果合理,我就从这个位置开始dfs,判断这个位置下面的三个位置,然后把这个最小路径和给弄上来;如果恰好遍历到了最后一行,那么根据这种写法,只能返回0;否则,返回一个比较大的数(特别注意这里不能是Integer.MIN_VALUE),我就吃了很多亏,因为你这里返回一个最大数,到了递归的上一层,还需要加上a[row][col],那么这个数最终什么样就不得而知了,但肯定会影响结果。

然后,因为开始位置是在第一行里面随机挑选的,因此我最终只需要遍历第一行就行了,选出每个位置的最小值,就是我们要的答案。

1 public int minFallingPathSum(int[][] matrix) {
2         int n = matrix.length;
3         int ans = Integer.MAX_VALUE;
4         for (int j = 0; j < n; ++j) {
5             int temp = dfs(0, j, matrix);
6             ans = Math.min(ans, temp);
7         }
8         return ans;
9 }

这样就行了。

后来拿去一提交,就emo了。。。感觉不对啊,为什么转了那么久的圈圈,还没有出来结果呢?

啪!超时了。。。我敲!

后来一看题解,其实这题可以用动态规划来写。

我们定义一个二位dp,如dp[i][j],这个就表示到达坐标(i,j)的最小路径和。然后,我们只需要注意几点情况:1.对于第一行的数据,最小路径和就是其本身,这一点无可厚非;2.对于所有第一列和最后一列的数据,我们只有两个可能的来源需要考虑,例如对于第一列的数据,我们只需考虑这个数据上一行同列的数据和上一行右边一列的数据,这一点注意清楚就好了;3.边界条件考虑清楚了,那对于其他的数据就有三个来源方向考虑,这个就很简单了,根据题目意思来即可。

核心代码:

 1 public int minFallingPathSum(int[][] matrix) {
 2         int row = matrix.length;
 3         int col = matrix[0].length;
 4         int[][] dp = new int[row][col];
 5         for (int i = 0; i < row; ++i) {
 6             for (int j = 0; j < col; ++j) {
 7                 if (i == 0) {
 8                     dp[i][j] = matrix[i][j];
 9                 } else if (j == 0) {
10                     dp[i][j] = Math.min(dp[i-1][j], dp[i-1][j+1]) + matrix[i][j];
11                 } else if (j == col - 1) {
12                     dp[i][j] = Math.min(dp[i-1][j-1], dp[i-1][j]) + matrix[i][j];
13                 } else {
14                     dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i-1][j-1]), dp[i-1][j+1]) + matrix[i][j];
15                 }
16             }
17         }
18         int ans = Integer.MAX_VALUE;
19         for (int j = 0; j < col; ++j) {
20             ans = Math.min(ans, dp[row-1][j]);
21         }
22         return ans;
23     }

顺便提一句,本人在重写代码的时候,除了i==0的情况,其他三种情况全忘记加matrix[i][j]了[QAQ].


下面再来一道题,相当于是这道题的变形:

题面

这道题呢,采用跟上一道题几乎一模一样的解法:

 1 public int minimumTotal(List> triangle) {
 2         int n = triangle.size();
 3         int[][] dp = new int[n][n];
 4         int temp = 1;
 5         for (int i = 0; i < n; ++i) {
 6             for (int j = 0; j < temp; ++j) {
 7                 if (i == 0) {
 8                     dp[i][j] = triangle.get(i).get(i);
 9                 } else if (j == 0) {
10                     dp[i][j] = dp[i-1][j] + triangle.get(i).get(j);
11                 } else if (j == temp - 1) {
12                     dp[i][j] = dp[i-1][j-1] + triangle.get(i).get(j);
13                 } else {
14                     dp[i][j] = Math.min(dp[i-1][j], dp[i-1][j-1]) + triangle.get(i).get(j);
15                 }
16             }
17             ++temp;
18         }
19         int ans = Integer.MAX_VALUE;
20         for (int j = 0; j < temp; ++j) {
21             ans = Math.min(ans, dp[n-1][j]);
22         }
23         return ans;
24     }

无非就是对于列数的处理与上一道题不同,直接开个新变量,每次列遍历完,在遍历下一行之前让列数加1就行了,其他几乎没怎么变,边界条件也跟上一道题一样。

但是你会发现上面的代码有问题,问题出在哪呢?

就出在用来遍历列而开的一个临时变量temp上。试想一下,这个temp初始值是1,每次遍历完一行就加了1,到最后全部遍历结束时,是不是这个temp是比triangle的size还要大的?大多少呢?只大了1.然后我们再去寻找答案的时候,你把temp作为最终j不能超过的那个数,肯定会数组越界的。

我们在最后把这个temp变成n就好了。

其实根本不用这么麻烦,本人只是编程技术太不熟练,才搞了这么多幺蛾子出来,你完全可以采用for (int j = 0; j < i; ++j)这样的写法,不用专门搞一个临时变量出来。