We have a square grid of numbers, and we need to find a path from the top row to the bottom row that has the smallest possible sum. The movement rule constrains the choices: from any cell, you can only move to the cell directly below, diagonally below-left, or diagonally below-right. At each row, that gives up to three choices for where to step next.
This is a dynamic programming problem. The minimum falling path ending at any cell in row i depends only on the minimum falling paths ending at the neighboring cells in row i - 1. There is optimal substructure (the best path to a cell builds on the best path to cells above it) and overlapping subproblems (multiple paths converge on the same cell).
We do not need to track the full path. For each cell, we only need the minimum cost to reach it from any cell in the first row.
1 <= n <= 100 → At most 10,000 cells, so an O(n^2) solution is well within limits. The path sum stays within int range (at most 100 cells times 100 each, so 10,000 in magnitude), so no overflow handling is needed.-100 <= matrix[i][j] <= 100 → Values can be negative, which rules out a greedy approach. Always picking the smallest neighbor can miss the optimum, because a locally larger value can sit above two very negative cells and lead to a smaller total. Every path has to be evaluated through the recurrence.Try every possible falling path and return the one with the smallest sum. From each cell in the first row, recursively explore all three options in the next row (below-left, below, below-right), accumulating the sum along the way. Reaching the last row gives a complete path sum.
This is a depth-first search over all possible paths. For each of the n starting positions in the first row, the search branches into up to 3 choices per row, forming a tree of possibilities.
The bottleneck is recomputing the same (row, col) subproblems repeatedly. The next approach caches each result the first time it is computed.
The brute force recurrence is correct, but it recomputes the same cells many times. Caching the result of dfs(row, col) after the first computation lets every later call to the same (row, col) return immediately. This memoization turns the exponential solution into a polynomial one.
There are at most n * n unique (row, col) pairs, and each does O(1) work after looking up its children, so the total work drops from O(3^n) to O(n^2).
Memoization reaches O(n^2) time, which is optimal here. The next approach fills the same table iteratively, removing the recursion overhead.
Build the table iteratively from the top row downward instead of recursing. Define dp[i][j] as the minimum falling path sum ending at cell (i, j). The first row of the DP table equals the first row of the matrix. For every subsequent row, each cell's value is its matrix value plus the minimum of the up-to-three cells directly above it.
Once the entire table is filled, the answer is the minimum value in the last row. This is the same recurrence as the memoized solution, computed in a different order. Filling it iteratively avoids recursion overhead and sets up the space optimization in the next approach.
Computing row i only reads from row i-1, so the rows above i-1 are never used again. The next approach keeps only the previous row instead of the entire table, dropping space from O(n^2) to O(n).
Since each row of the DP table only depends on the immediately previous row, two 1D arrays suffice instead of a full 2D table: one holds the previous row's results and one holds the row being computed. After processing a row, the current row becomes the previous row for the next iteration. Writing into a separate curr array keeps prev intact while it is still being read, so the three lookups for each cell always see the previous row's final values.
This is the rolling-array optimization for grid DP, where each row depends only on the one above it.
prev array with the first row of the matrix.curr array.