# 931. Minimum Falling Path Sum

**<font color=red>难度: Medium</font>**

## 刷题内容

> 原题链接

https://leetcode.com/problems/minimum-falling-path-sum/description/

> 内容描述
```
Given a square array of integers A, we want the minimum sum of a falling path through A.

A falling path starts at any element in the first row, and chooses one element from each row.  The next row's choice must be in a column that is different from the previous row's column by at most one.

 

Example 1:

Input: [[1,2,3],[4,5,6],[7,8,9]]
Output: 12
Explanation: 
The possible falling paths are:
[1,4,7], [1,4,8], [1,5,7], [1,5,8], [1,5,9]
[2,4,7], [2,4,8], [2,5,7], [2,5,8], [2,5,9], [2,6,8], [2,6,9]
[3,5,7], [3,5,8], [3,5,9], [3,6,8], [3,6,9]
The falling path with the smallest sum is [1,4,7], so the answer is 12.

 

Note:

1 <= A.length == A[0].length <= 100
-100 <= A[i][j] <= 100
```


## 解题方案

> 思路 1
******- 时间复杂度: O(row * col^2)******- 空间复杂度: O(row * col)******


上来写了个递归，超时，真的蠢。。

```
class Solution(object):
    def minFallingPathSum(self, A):
        """
        :type A: List[List[int]]
        :rtype: int
        """
        def find(A, idx):
            if not A or len(A) == 0:
                return 0
            
            if idx == 0:
                lookup[len(A), idx] = min(A[0][0]+find(A[1:], 0), A[0][1]+find(A[1:], 1))
                return lookup[len(A), idx]
            elif idx == len(A[0])-1:
                lookup[len(A), idx] = min(A[0][-1]+find(A[1:], idx), A[0][-2]+find(A[1:], idx-1))
                return lookup[len(A), idx]
            else:
                lookup[len(A), idx] = min(A[0][idx-1]+find(A[1:],idx-1),A[0][idx]+find(A[1:],idx),A[0][idx+1]+find(A[1:],idx+1))
                return lookup[len(A), idx]
        
        res = sys.maxsize
        lookup = {}
        for idx, num in enumerate(A[0]):
            if (len(A[1:]), idx) in lookup:
                res = min(res, num+lookup[len(A[1:]), idx])
            else:
                res = min(res, num+find(A[1:], idx))
            
        return res
```

> 思路 2
******- 时间复杂度: O(row * col)******- 空间复杂度: O(row * col)******

先梳理下要求。
给一个数组，数组从上至下，想找到一条路径 A，这条路径落到最下面的时候的总和是最小的。
每一次下落可选的点是，左边，自身，右边。

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

若从 1 这个点开始，    
那么第二次下落可选的点为 [4, 5]，1 是最左边了，没有更左边一位的数了。
第三次若选 4 则可选的点为 [7, 8]，同上。
      若选 5 则可选的点为 [7, 8, 9] 左 中 右。


1. 一开始可能想到的是贪心的，只能从最上面开始下落，那一开始就选最小的，然后不断选最小的不就可以了吗？
对于所有的情况，贪心往往得到的是次优解，而不是最优解。

2. 能想到贪心，进一步可以先到动态规划（DP）。
对于每一个点来说，要想保证它是当前最小的，那么就需要在它上面的 左 中 右 三个点里找到一个最小的值给自己。

从下至上我们来看，4 可以选择的点为 [1, 2]，那么它要想保证加上一个值最小，要选的只能是 1 。
                5 则可以选的点为 [1, 2, 3]，也是 1。
                6 可以是 [2, 3]，那么是 3。

这一层的子问题解决完之后变成了 [5(4+1), 6(5+1), 8(6+2)]。

在开始下一层，7 是 [5, 6] 里选择。
             8 是 [5, 6, 7] 里选择。
             

```python
class Solution(object):
    def minFallingPathSum(self, A):
        """
        :type A: List[List[int]]
        :rtype: int
        """
        if not A or len(A) == 0 or len(A[0]) == 0:
            return 0
        
        row = len(A)
        col = len(A[0]) if row else 0
            
        dp = [[0] * col for i in range(row)]
        dp[0] = A[0]
            
        for i in range(1, row):
            for j in range(col):
                if j == 0:
                    dp[i][j] = min(dp[i-1][0], dp[i-1][1]) + A[i][j]
                elif j == col-1:
                    dp[i][j] = min(dp[i-1][-1], dp[i-1][-2]) + A[i][j]
                else:
                    dp[i][j] = min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1]) + A[i][j]

        return min(dp[-1])
```

> 思路 3
******- 时间复杂度: O(row * col)******- 空间复杂度: O(1)******

可以写的更加简洁巧妙一些, 优化空间到O(1)


```python
class Solution(object):
    def minFallingPathSum(self, A):
        """
        :type A: List[List[int]]
        :rtype: int
        """
        for i in range(1, len(A)):
            for j in range(len(A[0])):
                prev_left = A[i-1][j-1] if j-1 >= 0 else float('inf')
                prev_right = A[i-1][j+1] if j+1 < len(A[0]) else float('inf')
                A[i][j] += min(A[i-1][j], prev_left, prev_right)
        return min(A[-1])
```

