<p>Given an <code>n x n</code> array of integers <code>matrix</code>, return <em>the <strong>minimum sum</strong> of any <strong>falling path</strong> through</em> <code>matrix</code>.</p>

<p>A <strong>falling path</strong> starts at any element in the first row and chooses the element in the next row that is either directly below or diagonally left/right. Specifically, the next element from position <code>(row, col)</code> will be <code>(row + 1, col - 1)</code>, <code>(row + 1, col)</code>, or <code>(row + 1, col + 1)</code>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/11/03/failing1-grid.jpg" style="width: 499px; height: 500px;" />
<pre>
<strong>Input:</strong> matrix = [[2,1,3],[6,5,4],[7,8,9]]
<strong>Output:</strong> 13
<strong>Explanation:</strong> There are two falling paths with a minimum sum as shown.
</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/11/03/failing2-grid.jpg" style="width: 164px; height: 365px;" />
<pre>
<strong>Input:</strong> matrix = [[-19,57],[-40,-5]]
<strong>Output:</strong> -59
<strong>Explanation:</strong> The falling path with a minimum sum is shown.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>n == matrix.length == matrix[i].length</code></li>
	<li><code>1 &lt;= n &lt;= 100</code></li>
	<li><code>-100 &lt;= matrix[i][j] &lt;= 100</code></li>
</ul>

class Solution:
    def minFallingPathSum(self, matrix: List[List[int]]) -> int:
        self.n = len(matrix)
        
        self.dp: dict[int, dict[int, int]] = {}
        
        def rec(row: int, prev_col: int) -> int:
            if row == self.n - 1:
                best = sys.maxsize
                
                if prev_col > 0:
                    best = min(best, matrix[row][prev_col - 1])
                if prev_col < self.n - 1:
                    best = min(best, matrix[row][prev_col + 1])
                
                best = min(best, matrix[row][prev_col])
                
                return best
            else:
                best = sys.maxsize
                try_row = row + 1
                
                def run_rec(try_row: int, try_col: int) -> int:
                    if try_row in self.dp and try_col in self.dp[try_row]:
                        rec_res = self.dp[try_row][try_col]
                    else:
                        rec_res = rec(row + 1, try_col)
                        if try_row not in self.dp:
                            self.dp[try_row] = {}
                        self.dp[try_row][try_col] = rec_res
                    
                    return rec_res
                
                if prev_col > 0:
                    try_col = prev_col - 1
                    rec_res = run_rec(try_row, try_col)
                    best = min(best, rec_res + matrix[row][try_col])
                if prev_col < self.n - 1:
                    try_col = prev_col + 1
                    rec_res = run_rec(try_row, try_col)
                    best = min(best, rec_res + matrix[row][try_col])
                
                try_col = prev_col
                rec_res = run_rec(try_row, try_col)
                best = min(best, rec_res + matrix[row][try_col])
                
                return best
        
        res = sys.maxsize
        for col in range(self.n):
            res = min(res, rec(0, col))
        
        return res
                    
20700