Skip to content

64. Minimum Path Sum

class Solution:
    def minPathSum(self, grid: list[list[int]]) -> int:
        """Problem:.

        Given an m x n grid of non-negative integers, find a path from the
        top-left to the bottom-right corner that minimizes the sum of values
        along the path.

        Constraints:
        - You can only move either right or down at any point.
        - 0 <= grid[i][j] <= 200

        ------------------------------------------------------------
        Dynamic Programming Approach (Space Optimized)

        Idea:
        Each cell (i, j) can be reached either:
        - from above (i-1, j)
        - from the left (i, j-1)

        So the recurrence is:
            dp[i][j] = grid[i][j] + min(dp[i-1][j], dp[i][j-1])

        Instead of storing the full m x n DP table, we observe that:
        - To compute the current row, we only need:
            * the previous row (dp[i-1][j])
            * the current row's previous value (dp[i][j-1])

        This allows us to compress the DP table into a single array.

        ------------------------------------------------------------
        State Definition (1D Optimization):

        Let `state[j]` represent the minimum path sum to reach column `j`
        in the current row.

        During iteration:
        - Before updating `state[j]`, it stores the value from the previous row
          → represents dp[i-1][j] (from up)
        - After updating `state[j-1]`, it represents dp[i][j-1] (from left)

        Transition:
            state[j] = grid[i][j] + min(state[j], state[j-1])

            where:
            - state[j]   → from up
            - state[j-1] → from left

        ------------------------------------------------------------
        Initialization:

        First cell:
            state[0] = grid[0][0]

        First row (can only come from left):
            state[j] = state[j-1] + grid[0][j]

        First column (can only come from above):
            state[0] += grid[i][0]  (for each new row)

        ------------------------------------------------------------
        Complexity:

        Time:  O(m * n)
        Space: O(n)   (optimized from O(m * n))

        ------------------------------------------------------------
        Key Insight:

        We can reuse a single array because each state depends only on:
        - the previous row (old value of state[j])
        - the current row (updated value of state[j-1])

        The left-to-right iteration order ensures we do not overwrite
        values before they are used.
        """
        m, n = len(grid), len(grid[0])
        state = [0] * n

        state[0] = grid[0][0]

        # Initialize first row
        for j in range(1, n):
            state[j] = grid[0][j] + state[j - 1]

        # Process remaining rows
        for i in range(1, m):
            state[0] += grid[i][0]
            for j in range(1, n):
                state[j] = grid[i][j] + min(state[j - 1], state[j])

        return state[-1]