2 minute read

Problem Statement

problem-1289

Notes:

  • need to review this again

Topdown Memoization - TLE

class Solution:
    def minFallingPathSum(self, grid: List[List[int]]) -> int:
        N = len(grid)
        res = float('inf')
        @cache
        def dfs(row, curr_sum, prev_col):
            if row >= N:
                return curr_sum
            re = float('inf')
            for col in range(N):
                if col != prev_col:
                    re = min(re, dfs(row + 1, curr_sum + grid[row][col], col))
            return re if re != float('inf') else curr_sum


        return dfs(0, 0, N)

Editorial Solution

Approach 1: Top-Down Dynamic Programming

class Solution:
    def minFallingPathSum(self, grid: List[List[int]]) -> int:
        # Save the size of the square grid
        n = len(grid)

        # Initialize a hash map to cache the result of each sub-problem
        memo = {}

        # The optimal(row, col) function returns the minimum sum of a
        # falling path with non-zero shifts, starting from grid[row][col]
        def optimal(row, col):
            # If the last row, then return the value of the cell itself
            if row == n - 1:
                return grid[row][col]

            # If the result of this sub-problem is already cached
            if (row, col) in memo:
                return memo[(row, col)]

            # Select grid[row][col], and move on to next row. For next
            # row, choose the cell that leads to the minimum sum
            next_minimum = inf
            for next_row_col in range(n):
                if next_row_col != col:
                    next_minimum = min(next_minimum, optimal(row + 1, next_row_col))

            # Minimum cost from this cell
            memo[(row, col)] = grid[row][col] + next_minimum
            return memo[(row, col)]

        # We can select any element from the first row. We will select
        # the element which leads to minimum sum.
        answer = inf
        for col in range(n):
            answer = min(answer, optimal(0, col))

        # Return the minimum sum
        return answer

Approach 2: Bottom-Up Dynamic Programming

class Solution:
    def minFallingPathSum(self, grid: List[List[int]]) -> int:
        # Save the size of the square grid
        n = len(grid)

        # Initialize a two-dimensional array to cache the result of each sub-problem
        memo = [[inf] * n for _ in range(n)]

        # Fill the base case
        for col in range(n):
            memo[n - 1][col] = grid[n - 1][col]

        # Fill the recursive cases
        for row in range(n - 2, -1, -1):
            for col in range(n):
                # Select minimum from valid cells of the next row
                next_minimum = inf
                for next_row_col in range(n):
                    if next_row_col != col:
                        next_minimum = min(next_minimum, memo[row + 1][next_row_col])

                # Minimum cost from this cell
                memo[row][col] = grid[row][col] + next_minimum

        # Find the minimum from the first row
        answer = inf
        for col in range(n):
            answer = min(answer, memo[0][col])

        # Return the answer
        return answer