64. Minimum Path Sum
Problem Description
You are given a m x n
grid filled with non-negative numbers. Your task is to find a path from the top-left corner of the grid to the bottom-right corner that minimizes the sum of all numbers along the path.
The movement constraints are:
- You can only move either down or right at any point in time
- You cannot move up or left
- You cannot move diagonally
For example, if you have a grid like:
[1, 3, 1] [1, 5, 1] [4, 2, 1]
Starting from position (0,0)
with value 1
, you need to reach position (2,2)
with value 1
. One possible path could be: 1 → 3 → 1 → 1 → 1
(going right, right, down, down), which gives a sum of 7
. Another path could be: 1 → 1 → 4 → 2 → 1
(going down, down, right, right), which gives a sum of 9
. The optimal path would be: 1 → 3 → 1 → 1 → 1
with the minimum sum of 7
.
The goal is to determine this minimum possible sum for any given grid.
Intuition
The key insight is that to reach any cell (i, j)
in the grid, we can only come from two possible directions: either from the cell directly above it (i-1, j)
or from the cell directly to its left (i, j-1)
. This is because we can only move down or right.
This observation leads us to think about the problem in terms of subproblems. If we know the minimum path sum to reach (i-1, j)
and the minimum path sum to reach (i, j-1)
, then the minimum path sum to reach (i, j)
would be:
min(minimum_sum_to_reach(i-1, j), minimum_sum_to_reach(i, j-1)) + grid[i][j]
We take the minimum of the two possible paths and add the current cell's value.
This naturally suggests a dynamic programming approach where we build up the solution progressively. Starting from the top-left corner, we can calculate the minimum path sum for each cell by using the already computed minimum path sums of the cells we could have come from.
For the first row and first column, there's only one way to reach each cell:
- For cells in the first row, we can only come from the left
- For cells in the first column, we can only come from above
By systematically computing the minimum path sum for each cell from top-left to bottom-right, we eventually find the minimum path sum to reach the bottom-right corner, which is our answer.
Learn more about Dynamic Programming patterns.
Solution Approach
We implement the solution using dynamic programming with a 2D array f
where f[i][j]
represents the minimum path sum from the top-left corner (0, 0)
to position (i, j)
.
Step 1: Initialize the DP array
- Create a 2D array
f
with the same dimensions as the grid (m x n
) - Set
f[0][0] = grid[0][0]
as the starting point
Step 2: Fill the first column
- For each cell in the first column (where
j = 0
), we can only arrive from the cell above - Therefore:
f[i][0] = f[i-1][0] + grid[i][0]
for alli
from 1 tom-1
- This represents the cumulative sum going straight down
Step 3: Fill the first row
- For each cell in the first row (where
i = 0
), we can only arrive from the cell to the left - Therefore:
f[0][j] = f[0][j-1] + grid[0][j]
for allj
from 1 ton-1
- This represents the cumulative sum going straight right
Step 4: Fill the rest of the grid
- For any other cell
(i, j)
where bothi > 0
andj > 0
, we can arrive from either:- The cell above:
f[i-1][j]
- The cell to the left:
f[i][j-1]
- The cell above:
- We choose the path with the minimum sum:
f[i][j] = min(f[i-1][j], f[i][j-1]) + grid[i][j]
Step 5: Return the result
- The minimum path sum to reach the bottom-right corner is stored in
f[m-1][n-1]
- We can access this using
f[-1][-1]
in Python
The time complexity is O(m × n)
as we visit each cell once, and the space complexity is also O(m × n)
for the DP array.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let me walk through a small example to illustrate the solution approach using this grid:
[1, 3, 1] [1, 5, 1] [4, 2, 1]
Step 1: Initialize the DP array
Create a 3×3 DP array f
and set the starting point:
f[0][0] = grid[0][0] = 1 f = [1, ?, ?] [?, ?, ?] [?, ?, ?]
Step 2: Fill the first column
For cells in the first column, we can only come from above:
f[1][0] = f[0][0] + grid[1][0] = 1 + 1 = 2
f[2][0] = f[1][0] + grid[2][0] = 2 + 4 = 6
f = [1, ?, ?] [2, ?, ?] [6, ?, ?]
Step 3: Fill the first row
For cells in the first row, we can only come from the left:
f[0][1] = f[0][0] + grid[0][1] = 1 + 3 = 4
f[0][2] = f[0][1] + grid[0][2] = 4 + 1 = 5
f = [1, 4, 5] [2, ?, ?] [6, ?, ?]
Step 4: Fill the remaining cells
For each remaining cell, choose the minimum path from above or left:
Position (1,1):
- From above:
f[0][1] = 4
- From left:
f[1][0] = 2
f[1][1] = min(4, 2) + grid[1][1] = 2 + 5 = 7
Position (1,2):
- From above:
f[0][2] = 5
- From left:
f[1][1] = 7
f[1][2] = min(5, 7) + grid[1][2] = 5 + 1 = 6
Position (2,1):
- From above:
f[1][1] = 7
- From left:
f[2][0] = 6
f[2][1] = min(7, 6) + grid[2][1] = 6 + 2 = 8
Position (2,2):
- From above:
f[1][2] = 6
- From left:
f[2][1] = 8
f[2][2] = min(6, 8) + grid[2][2] = 6 + 1 = 7
Final DP array:
f = [1, 4, 5] [2, 7, 6] [6, 8, 7]
Step 5: Return the result
The minimum path sum is f[2][2] = 7
, which corresponds to the path: 1 → 3 → 1 → 1 → 1
(right, right, down, down).
Solution Implementation
1class Solution:
2 def minPathSum(self, grid: List[List[int]]) -> int:
3 """
4 Find the minimum path sum from top-left to bottom-right corner.
5 Movement is only allowed down or right at any point.
6
7 Args:
8 grid: 2D list of non-negative integers
9
10 Returns:
11 Minimum sum of all numbers along the path
12 """
13 # Get dimensions of the grid
14 rows, cols = len(grid), len(grid[0])
15
16 # Initialize DP table to store minimum path sum to each cell
17 dp = [[0] * cols for _ in range(rows)]
18
19 # Base case: starting position
20 dp[0][0] = grid[0][0]
21
22 # Initialize first column (can only come from above)
23 for row in range(1, rows):
24 dp[row][0] = dp[row - 1][0] + grid[row][0]
25
26 # Initialize first row (can only come from left)
27 for col in range(1, cols):
28 dp[0][col] = dp[0][col - 1] + grid[0][col]
29
30 # Fill the DP table for remaining cells
31 # Each cell's minimum path sum is the minimum of coming from above or left
32 for row in range(1, rows):
33 for col in range(1, cols):
34 dp[row][col] = min(dp[row - 1][col], dp[row][col - 1]) + grid[row][col]
35
36 # Return the minimum path sum to reach bottom-right corner
37 return dp[-1][-1]
38
1class Solution {
2 public int minPathSum(int[][] grid) {
3 // Get dimensions of the grid
4 int rows = grid.length;
5 int cols = grid[0].length;
6
7 // Create a DP table to store minimum path sum to each cell
8 int[][] dp = new int[rows][cols];
9
10 // Initialize the starting cell (top-left corner)
11 dp[0][0] = grid[0][0];
12
13 // Initialize the first column (can only come from above)
14 for (int i = 1; i < rows; i++) {
15 dp[i][0] = dp[i - 1][0] + grid[i][0];
16 }
17
18 // Initialize the first row (can only come from left)
19 for (int j = 1; j < cols; j++) {
20 dp[0][j] = dp[0][j - 1] + grid[0][j];
21 }
22
23 // Fill the rest of the DP table
24 // For each cell, choose the minimum path from either top or left
25 for (int i = 1; i < rows; i++) {
26 for (int j = 1; j < cols; j++) {
27 dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
28 }
29 }
30
31 // Return the minimum path sum to reach the bottom-right corner
32 return dp[rows - 1][cols - 1];
33 }
34}
35
1class Solution {
2public:
3 int minPathSum(vector<vector<int>>& grid) {
4 // Get dimensions of the grid
5 int rows = grid.size();
6 int cols = grid[0].size();
7
8 // Create a 2D DP table to store minimum path sum to each cell
9 // dp[i][j] represents the minimum path sum from (0,0) to (i,j)
10 vector<vector<int>> dp(rows, vector<int>(cols));
11
12 // Initialize the starting point
13 dp[0][0] = grid[0][0];
14
15 // Initialize the first column (can only come from above)
16 for (int i = 1; i < rows; ++i) {
17 dp[i][0] = dp[i - 1][0] + grid[i][0];
18 }
19
20 // Initialize the first row (can only come from left)
21 for (int j = 1; j < cols; ++j) {
22 dp[0][j] = dp[0][j - 1] + grid[0][j];
23 }
24
25 // Fill the rest of the DP table
26 // For each cell, choose the minimum path from either top or left
27 for (int i = 1; i < rows; ++i) {
28 for (int j = 1; j < cols; ++j) {
29 dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
30 }
31 }
32
33 // Return the minimum path sum to reach the bottom-right corner
34 return dp[rows - 1][cols - 1];
35 }
36};
37
1/**
2 * Finds the minimum path sum from top-left to bottom-right corner of a grid
3 * Movement is only allowed down or right at any point
4 * @param grid - 2D array of non-negative integers representing the grid
5 * @returns The minimum sum of all numbers along the path
6 */
7function minPathSum(grid: number[][]): number {
8 // Get grid dimensions
9 const rows: number = grid.length;
10 const cols: number = grid[0].length;
11
12 // Initialize DP table to store minimum path sum to each cell
13 const dp: number[][] = Array(rows)
14 .fill(0)
15 .map(() => Array(cols).fill(0));
16
17 // Base case: starting cell
18 dp[0][0] = grid[0][0];
19
20 // Initialize first column (can only come from above)
21 for (let row = 1; row < rows; row++) {
22 dp[row][0] = dp[row - 1][0] + grid[row][0];
23 }
24
25 // Initialize first row (can only come from left)
26 for (let col = 1; col < cols; col++) {
27 dp[0][col] = dp[0][col - 1] + grid[0][col];
28 }
29
30 // Fill the rest of the DP table
31 // For each cell, choose minimum path from either top or left
32 for (let row = 1; row < rows; row++) {
33 for (let col = 1; col < cols; col++) {
34 dp[row][col] = Math.min(dp[row - 1][col], dp[row][col - 1]) + grid[row][col];
35 }
36 }
37
38 // Return minimum path sum to bottom-right corner
39 return dp[rows - 1][cols - 1];
40}
41
Time and Space Complexity
The time complexity is O(m × n)
, where m
is the number of rows and n
is the number of columns in the grid. This is because:
- Initializing the first cell
f[0][0]
takesO(1)
time - Filling the first column requires iterating through
m-1
cells, takingO(m)
time - Filling the first row requires iterating through
n-1
cells, takingO(n)
time - The nested loops iterate through
(m-1) × (n-1)
cells, with each cell computation takingO(1)
time - Overall:
O(1) + O(m) + O(n) + O(m × n) = O(m × n)
The space complexity is O(m × n)
because the algorithm creates a 2D array f
with dimensions m × n
to store the minimum path sum for each cell. Each cell in this array stores a single integer value, resulting in total space usage proportional to the grid size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Modifying the Original Grid Instead of Using Separate DP Array
A common mistake is trying to save space by modifying the input grid directly to store the DP values. This can lead to issues if the original grid needs to be preserved or if the function is called multiple times with the same grid reference.
Problematic Code:
def minPathSum(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
# Modifying grid directly - BAD PRACTICE
for row in range(1, rows):
grid[row][0] += grid[row - 1][0]
for col in range(1, cols):
grid[0][col] += grid[0][col - 1]
for row in range(1, rows):
for col in range(1, cols):
grid[row][col] += min(grid[row - 1][col], grid[row][col - 1])
return grid[-1][-1]
Solution: Always create a separate DP array unless explicitly stated that modifying the input is acceptable.
2. Incorrect Initialization of Boundaries
Another frequent error is forgetting to properly initialize the first row and column, or starting the loop indices incorrectly.
Problematic Code:
def minPathSum(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
dp = [[0] * cols for _ in range(rows)]
dp[0][0] = grid[0][0]
# WRONG: Starting from index 0 instead of 1
for row in range(0, rows): # Should be range(1, rows)
dp[row][0] = dp[row - 1][0] + grid[row][0] # This will cause IndexError
# Or forgetting to initialize boundaries at all
for row in range(1, rows):
for col in range(1, cols):
dp[row][col] = min(dp[row - 1][col], dp[row][col - 1]) + grid[row][col]
# This leaves first row and column (except [0][0]) as zeros
Solution: Carefully initialize boundaries before filling the interior cells, and ensure loop indices start from 1 for boundary initialization.
3. Space Optimization Pitfall When Using 1D Array
When optimizing space to O(n) using a 1D array, a common mistake is not properly updating values or using the wrong previous values.
Problematic Code:
def minPathSum(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
dp = [0] * cols
for row in range(rows):
for col in range(cols):
if row == 0 and col == 0:
dp[col] = grid[0][0]
elif row == 0:
dp[col] = dp[col - 1] + grid[row][col]
elif col == 0:
dp[col] = dp[col] + grid[row][col] # dp[col] holds value from previous row
else:
# WRONG: Not handling the update correctly
dp[col] = min(dp[col], dp[col - 1]) + grid[row][col]
Correct Space-Optimized Solution:
def minPathSum(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
dp = [float('inf')] * cols
dp[0] = 0
for row in range(rows):
for col in range(cols):
if col == 0:
dp[col] = dp[col] + grid[row][col]
else:
dp[col] = min(dp[col], dp[col - 1]) + grid[row][col]
return dp[-1]
Which of the following is a min heap?
Recommended Readings
What is Dynamic Programming Prerequisite DFS problems dfs_intro Backtracking problems backtracking Memoization problems memoization_intro Pruning problems backtracking_pruning Dynamic programming is an algorithmic optimization technique that breaks down a complicated problem into smaller overlapping sub problems in a recursive manner and uses solutions to the sub problems to construct a solution
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!