Travel & Candies — Grid Path DP
Grid path DP models a traveler moving from the top-left to the bottom-right of a matrix, taking only right or down steps. Cells hold costs, candies, or obstacles. This lesson covers unique paths (LeetCode 62), minimum path sum (64), maximum candies, and obstacles (63) — with full C, Java, and Python code and trace tables.
Overview
A robot (or traveler) starts at (0, 0) and must reach (m−1, n−1). From each cell it may move right or down only. The grid value at each cell contributes to the answer depending on the problem:
- Count paths — how many distinct routes exist?
- Minimum cost — smallest sum of cell values along any path
- Maximum candies — largest sum of rewards collected along any path
| Problem | Goal | Recurrence | LeetCode |
|---|---|---|---|
| Unique paths | Count routes | dp[i][j] = dp[i−1][j] + dp[i][j−1] | 62 |
| Unique paths II | Count with obstacles | Skip cell if blocked; else same sum | 63 |
| Minimum path sum | Min cost travel | dp[i][j] = min(top, left) + grid[i][j] | 64 |
| Maximum candies | Max reward path | dp[i][j] = max(top, left) + grid[i][j] | Custom / interview |
Sample grid (candies / costs):
col0 col1 col2
row0 1 3 1
row1 1 5 1
row2 4 2 1
Start S at (0,0) → Goal G at (2,2)
Allowed moves: → right or ↓ down
Min-cost path: 1 → 3 → 1 → 1 → 1 = 7
Max-candy path: 1 → 3 → 5 → 2 → 1 = 12
Unique Paths — Count Routes
Problem: An m × n grid with no obstacles — how many distinct paths from top-left to bottom-right?
State: dp[i][j] = number of paths to reach cell (i, j).
Recurrence: dp[i][j] = dp[i−1][j] + dp[i][j−1] (only one direction if on top or left edge).
Base: First row and first column are all 1 (only one way along each edge).
Minimum Path Sum — Min-Cost Travel
Problem (LeetCode 64): Each cell has a non-negative cost. Find the path from (0,0) to bottom-right with the minimum total cost.
Recurrence: dp[i][j] = min(dp[i−1][j], dp[i][j−1]) + grid[i][j]
Base: Fill first row and first column as cumulative sums from the start.
Minimum Path Sum Code (C, Java, Python)
Python
def min_path_sum(grid):
"""LeetCode 64 — minimum cost path top-left to bottom-right."""
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
dp[0][0] = grid[0][0]
for j in range(1, n):
dp[0][j] = dp[0][j - 1] + grid[0][j] # first row: only from left
for i in range(1, m):
dp[i][0] = dp[i - 1][0] + grid[i][0] # first col: only from top
for i in range(1, m):
for j in range(1, n):
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
return dp[m - 1][n - 1]
if __name__ == "__main__":
grid = [[1, 3, 1], [1, 5, 1], [4, 2, 1]]
print(f"min path sum = {min_path_sum(grid)}") # 7
Java
public class MinPathSum {
/** Minimum path sum — bottom-up 2D DP. */
public static int minPathSum(int[][] grid) {
int m = grid.length, n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
for (int j = 1; j < n; j++) dp[0][j] = dp[0][j - 1] + grid[0][j];
for (int i = 1; i < m; i++) dp[i][0] = dp[i - 1][0] + grid[i][0];
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
public static void main(String[] args) {
int[][] grid = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}};
System.out.println(minPathSum(grid)); // 7
}
}
C
#include <stdio.h>
#define MAX 100
int min2(int a, int b) { return a < b ? a : b; }
/* Minimum path sum on m x n grid */
int min_path_sum(int grid[MAX][MAX], int m, int n) {
int dp[MAX][MAX];
int i, j;
dp[0][0] = grid[0][0];
for (j = 1; j < n; j++) dp[0][j] = dp[0][j - 1] + grid[0][j];
for (i = 1; i < m; i++) dp[i][0] = dp[i - 1][0] + grid[i][0];
for (i = 1; i < m; i++) {
for (j = 1; j < n; j++) {
dp[i][j] = min2(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
int main(void) {
int grid[MAX][MAX] = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}};
printf("%d\n", min_path_sum(grid, 3, 3)); /* 7 */
return 0;
}
Minimum Path Sum Output & Trace
Program output (all three languages)
| Language | Input grid | Output | Optimal path |
|---|---|---|---|
| Python | [[1,3,1],[1,5,1],[4,2,1]] | 7 | (0,0)→(0,1)→(0,2)→(1,2)→(2,2) |
| Java | same 3×3 grid | 7 | 1+3+1+1+1 = 7 |
| C | same 3×3 grid | 7 | Identical DP table |
DP table trace — minimum path sum
| Cell (i,j) | grid[i][j] | dp[i][j] | From | Explanation |
|---|---|---|---|---|
| (0,0) | 1 | 1 | start | Base |
| (0,1) | 3 | 4 | left | 1+3 |
| (0,2) | 1 | 5 | left | 4+1 |
| (1,0) | 1 | 2 | top | 1+1 |
| (1,1) | 5 | 7 | top (2<4) | min(2,4)+5 |
| (1,2) | 1 | 6 | top (5<7) | min(5,7)+1 |
| (2,0) | 4 | 6 | top | 2+4 |
| (2,1) | 2 | 8 | top (6<7) | min(6,7)+2 |
| (2,2) | 1 | 7 | left (6<8) | min(8,6)+1 → answer 7 |
Maximum Candies — Best Reward Path
Problem: Each cell holds candies (non-negative). Collect all candies on the chosen path. Maximize the total from start to goal.
Recurrence: Same grid skeleton — replace min with max:
dp[i][j] = max(dp[i−1][j], dp[i][j−1]) + grid[i][j]
Maximum Candies Code (C, Java, Python)
Python
def max_candies(grid):
"""Maximum candies collected on a right/down path."""
m, n = len(grid), len(grid[0])
dp = [[0] * n for _ in range(m)]
dp[0][0] = grid[0][0]
for j in range(1, n):
dp[0][j] = dp[0][j - 1] + grid[0][j]
for i in range(1, m):
dp[i][0] = dp[i - 1][0] + grid[i][0]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
return dp[m - 1][n - 1]
if __name__ == "__main__":
grid = [[1, 3, 1], [1, 5, 1], [4, 2, 1]]
print(f"max candies = {max_candies(grid)}") # 12
Java
public class MaxCandies {
public static int maxCandies(int[][] grid) {
int m = grid.length, n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
for (int j = 1; j < n; j++) dp[0][j] = dp[0][j - 1] + grid[0][j];
for (int i = 1; i < m; i++) dp[i][0] = dp[i - 1][0] + grid[i][0];
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
public static void main(String[] args) {
int[][] grid = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}};
System.out.println(maxCandies(grid)); // 12
}
}
C
#include <stdio.h>
#define MAX 100
int max2(int a, int b) { return a > b ? a : b; }
int max_candies(int grid[MAX][MAX], int m, int n) {
int dp[MAX][MAX];
int i, j;
dp[0][0] = grid[0][0];
for (j = 1; j < n; j++) dp[0][j] = dp[0][j - 1] + grid[0][j];
for (i = 1; i < m; i++) dp[i][0] = dp[i - 1][0] + grid[i][0];
for (i = 1; i < m; i++) {
for (j = 1; j < n; j++) {
dp[i][j] = max2(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
int main(void) {
int grid[MAX][MAX] = {{1, 3, 1}, {1, 5, 1}, {4, 2, 1}};
printf("%d\n", max_candies(grid, 3, 3)); /* 12 */
return 0;
}
Maximum Candies Output & Trace
Program output
| Language | Input | Output | Best path (candies) |
|---|---|---|---|
| Python | [[1,3,1],[1,5,1],[4,2,1]] | 12 | 1→3→5→2→1 via (0,0)(0,1)(1,1)(2,1)(2,2) |
| Java / C | same grid | 12 | 1+3+5+2+1 = 12 |
DP table trace — maximum candies (final dp values)
| col 0 | col 1 | col 2 | |
|---|---|---|---|
| row 0 | 1 | 4 | 5 |
| row 1 | 2 | 9 | 10 |
| row 2 | 6 | 11 | 12 |
min vs max) completely changes the optimal path.
Obstacles & Blocked Cells
Unique Paths II (LeetCode 63): Cells marked as obstacles (1) cannot be entered. If start or goal is blocked, return 0.
Modification: When grid[i][j] == 1, set dp[i][j] = 0. Otherwise use the usual sum (count), min, or max recurrence.
| Grid (0=open, 1=block) | Unique paths | Explanation |
|---|---|---|
| [[0,0,0],[0,1,0],[0,0,0]] | 2 | Must detour around center obstacle |
| [[0,1],[0,0]] | 0 | Start cell blocked |
| [[1,1,1],[1,0,1],[1,1,1]] | 0 | Only center open — cannot reach corner from start |
See also unique paths in grid (sidebar topic) for extended grid variants including cherry pickup and dungeon game.
Space Optimization
Because each cell depends only on the cell above and to the left, the full m × n table can be reduced to a single row (or two rows):
2D: dp[i][j] = f(dp[i-1][j], dp[i][j-1]) + grid[i][j] 1D: dp[j] = f(dp[j], dp[j-1]) + grid[i][j] // update left→right per row Space: O(m×n) → O(n) Time stays O(m×n)
More Examples
Example 1: Unique paths 3×3 (no obstacles)
| m × n | DP answer | Formula C(m+n−2,m−1) | Paths |
|---|---|---|---|
| 3 × 3 | 6 | C(4,2) = 6 | RRDD, RDRD, RDDR, DRRD, DRDR, DDRR |
| 3 × 7 | 28 | C(8,2) = 28 | LeetCode 62 sample |
Example 2: Single row grid
| grid | Min path sum | Max candies | Why |
|---|---|---|---|
| [[1, 2, 3, 4]] | 10 | 10 | Only one path — sum all cells |
Example 3: Complexity summary
| Problem | Time | Space (2D / 1D) | Notes |
|---|---|---|---|
| Unique paths | O(m × n) | O(m × n) / O(n) | Counting DP |
| Min / max path sum | O(m × n) | O(m × n) / O(n) | Weighted grid |
| With obstacles | O(m × n) | Same | Zero blocked cells |
| BFS alternative | O(m × n) | O(m × n) | Works for unweighted count; DP is cleaner |
Approach Comparison
| Approach | Time | Space | When to use |
|---|---|---|---|
| 2D DP table | O(m × n) | O(m × n) | Whiteboard clarity; path reconstruction |
| 1D rolling array | O(m × n) | O(n) | Production / space-constrained |
| Combinatorics | O(min(m,n)) | O(1) | Unique paths only, no obstacles |
| DFS + memo | O(m × n) | O(m × n) | Top-down alternative; same complexity |
| BFS / Dijkstra | O(m × n log …) | O(m × n) | When moves are not only right/down |
Real-Life Applications
Grid path DP models any journey on a map with fixed move rules and per-cell costs or rewards.
| Domain | Real scenario | Grid mapping | Variant |
|---|---|---|---|
| Navigation | Cheapest route through a city grid with toll roads | Intersections = cells; toll = cost | Min path sum |
| Warehouse robotics | Robot picking packages while moving east/south only | Shelf positions = grid; items = candies | Max candies |
| Game design | Player collecting coins on a platformer map | Tile rewards; obstacles block tiles | Max + obstacles |
| Manufacturing | Optimal path for a CNC drill moving row-by-row | Grid of operations; min time per cell | Min path sum |
| Logistics | Counting delivery routes on a block grid | Blocks = cells; count paths | Unique paths |
| Pipeline ETL | Processing stages in a DAG shaped like a grid | Stage cost accumulates along path | Min cost |
| VLSI routing | Manhattan routing between chip pins | Grid wires; avoid blocked layers | Obstacles + count |
| Tourism | Maximize sightseeing score on a one-way walking tour | Sights = candy values on cells | Max candies |
Interview Tips
- Confirm moves: only right/down? or four directions? (four directions → BFS or different DP)
- Clarify: count paths, min cost, or max reward?
- Draw a small 3×3 grid and fill the DP table before coding.
- Initialize first row and first column explicitly — common bug source.
- For obstacles: if
grid[i][j]is blocked, setdp[i][j]=0(count) or skip. - Mention O(n) space optimization with a 1D array after the 2D solution.
- Unique paths without obstacles: offer combinatorial formula C(m+n−2, m−1).
- Connect min path to 1D DP (single row) and to extended grid topics.
Key Takeaway
Grid path DP fills dp[i][j] from the top and left neighbors: + for counting, min for cheapest travel, max for most candies. Same loop order every time — only the combine function changes. Master the 3×3 trace and you can handle obstacles, space optimization, and interview follow-ups.
Next up: Subsequence problems · Longest increasing subsequence · Unique paths in grid