<p>Given an <code>n x n</code> integer matrix <code>grid</code>, return <em>the minimum sum of a <strong>falling path with non-zero shifts</strong></em>.</p>

<p>A <strong>falling path with non-zero shifts</strong> is a choice of exactly one element from each row of <code>grid</code> such that no two elements chosen in adjacent rows are in the same column.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/08/10/falling-grid.jpg" style="width: 244px; height: 245px;" />
<pre>
<strong>Input:</strong> grid = [[1,2,3],[4,5,6],[7,8,9]]
<strong>Output:</strong> 13
<strong>Explanation:</strong> 
The possible falling paths are:
[1,5,9], [1,5,7], [1,6,7], [1,6,8],
[2,4,8], [2,4,9], [2,6,7], [2,6,8],
[3,4,8], [3,4,9], [3,5,7], [3,5,9]
The falling path with the smallest sum is&nbsp;[1,5,7], so the answer is&nbsp;13.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> grid = [[7]]
<strong>Output:</strong> 7
</pre>

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

<ul>
	<li><code>n == grid.length == grid[i].length</code></li>
	<li><code>1 &lt;= n &lt;= 200</code></li>
	<li><code>-99 &lt;= grid[i][j] &lt;= 99</code></li>
</ul>

class Solution {
public:
    struct RowDetails
    {
        int mBestCol;
        int mBestNum;
        int mSecondCol;
        int mSecondNum;
    };

    int minFallingPathSum(vector<vector<int>>& grid) {

        if (grid.size() == 1)
        {
            return grid[0][0];
        }

        else if (grid.size() == 2)
        {
            int first = grid[0][0] + grid[1][1];
            int sec = grid[1][0] + grid[1][0];
            return min(first, sec);
        }

        RowDetails minInRow = MinInRow(grid, 0, -1);
        int first = minInRow.mBestNum + minFallingPath(grid, 1, minInRow.mBestCol);
        int second = minInRow.mSecondNum + minFallingPath(grid, 1, minInRow.mSecondCol);
        return min(first, second);
    }

    

    int minFallingPath(const vector<vector<int>>& grid, int row, int prevCol)
    {
        if (mMemo[row].find(prevCol) != mMemo[row].end())
            return mMemo[row][prevCol];
        RowDetails minInRow = MinInRow(grid, row, prevCol);
        if (row == grid.size() - 1)
        {
            mMemo[row][prevCol] = minInRow.mBestNum;
            return minInRow.mBestNum;
        }
        else
        {
            int first = minInRow.mBestNum + minFallingPath(grid, row + 1, minInRow.mBestCol);
            int second = minInRow.mSecondNum + minFallingPath(grid, row + 1, minInRow.mSecondCol);
            mMemo[row][prevCol] = min(first, second);
            return min(first, second);
        }
    }

    RowDetails MinInRow(const vector<vector<int>>& grid, int row, int prevCol)
    {
        auto& gridRow = grid[row];
        int minVal = std::numeric_limits<int>::max();
        int secondMin = std::numeric_limits<int>::max();
        int minCol = -1;
        int secondCol = -1;
        
        for (int i = 0; i<gridRow.size(); i++)
        {
            if (i == prevCol)
                continue;
            if (gridRow[i] < minVal)
            {
                secondMin = minVal;
                secondCol = minCol;
                minCol = i;
                minVal = gridRow[i];
            }
            else if (gridRow[i] < secondMin)
            {
                secondMin = gridRow[i];
                secondCol = i;
            }
        }
        // cout << "row " << row << " prevCol " << prevCol << " " << minCol << " " << minVal << " " << secondCol << " " << secondMin << endl;
        return {minCol, minVal, secondCol, secondMin};
    }

std::unordered_map<int, std::unordered_map<int, int>> mMemo;
};
19700