from heapq import heappush, heappop
class Solution:
def minPathCost(self, grid, moveCost):
m, n = len(grid), len(grid[0])
# dp[r][c] = min cost to reach (r, c)
dp = [[float('inf')] * n for _ in range(m)]
for c in range(n):
dp[0][c] = grid[0][c]
for r in range(1, m):
for c in range(n):
for prev_c in range(n):
prev_val = grid[r-1][prev_c]
cost = dp[r-1][prev_c] + moveCost[prev_val][c] + grid[r][c]
if cost < dp[r][c]:
dp[r][c] = cost
return min(dp[-1])
class Solution {
public:
int minPathCost(vector<vector<int>>& grid, vector<vector<int>>& moveCost) {
int m = grid.size(), n = grid[0].size();
vector<vector<int>> dp(m, vector<int>(n, INT_MAX));
for (int c = 0; c < n; ++c)
dp[0][c] = grid[0][c];
for (int r = 1; r < m; ++r) {
for (int c = 0; c < n; ++c) {
for (int prev_c = 0; prev_c < n; ++prev_c) {
int prev_val = grid[r-1][prev_c];
int cost = dp[r-1][prev_c] + moveCost[prev_val][c] + grid[r][c];
if (cost < dp[r][c])
dp[r][c] = cost;
}
}
}
return *min_element(dp[m-1].begin(), dp[m-1].end());
}
};
class Solution {
public int minPathCost(int[][] grid, int[][] moveCost) {
int m = grid.length, n = grid[0].length;
int[][] dp = new int[m][n];
for (int[] row : dp)
java.util.Arrays.fill(row, Integer.MAX_VALUE);
for (int c = 0; c < n; ++c)
dp[0][c] = grid[0][c];
for (int r = 1; r < m; ++r) {
for (int c = 0; c < n; ++c) {
for (int prev_c = 0; prev_c < n; ++prev_c) {
int prev_val = grid[r-1][prev_c];
int cost = dp[r-1][prev_c] + moveCost[prev_val][c] + grid[r][c];
if (cost < dp[r][c])
dp[r][c] = cost;
}
}
}
int res = Integer.MAX_VALUE;
for (int c = 0; c < n; ++c)
res = Math.min(res, dp[m-1][c]);
return res;
}
}
var minPathCost = function(grid, moveCost) {
const m = grid.length, n = grid[0].length;
const dp = Array.from({length: m}, () => Array(n).fill(Infinity));
for (let c = 0; c < n; ++c)
dp[0][c] = grid[0][c];
for (let r = 1; r < m; ++r) {
for (let c = 0; c < n; ++c) {
for (let prev_c = 0; prev_c < n; ++prev_c) {
const prev_val = grid[r-1][prev_c];
const cost = dp[r-1][prev_c] + moveCost[prev_val][c] + grid[r][c];
if (cost < dp[r][c])
dp[r][c] = cost;
}
}
}
return Math.min(...dp[m-1]);
};
You are given a 2D integer array grid
with m
rows and n
columns. Each cell grid[r][c]
contains a value. You also have a 2D array moveCost
where moveCost[x][y]
represents the cost to move from a cell with value x
in the current row to column y
in the next row.
Your task is to find the minimum path cost from any cell in the first row to any cell in the last row. A path consists of choosing one cell from each row, starting from the first row and moving down one row at a time. When moving from row i
and column j
to row i+1
and column k
, you must pay the cost moveCost[grid[i][j]][k]
.
The cost of a path is the sum of all the values of the chosen cells plus all the movement costs incurred between rows. You must return the minimum possible total path cost.
At first glance, the problem looks like a variant of the "minimum path sum" in a grid, but with a twist: the cost to move from one cell to another depends on both the value of the current cell and the column you move to in the next row. This means the cost is not fixed or uniform, and the path can start from any cell in the first row and end at any cell in the last row.
A brute-force approach would try all possible paths from the first row to the last, but this quickly becomes infeasible as the number of possible paths grows exponentially with the number of rows and columns.
To optimize, we recognize that the problem has an "overlapping subproblems" property, which is ideal for dynamic programming. For each cell in a given row, we can compute the minimum cost to reach it by considering all possible cells in the previous row. By storing and reusing these results, we avoid redundant calculations and significantly speed up the solution.
We use dynamic programming to solve this problem efficiently. Here’s how:
dp
where dp[r][c]
represents the minimum cost to reach cell (r, c)
in the grid.
dp
with the values of the first row of grid
, since there are no movement costs to start.
r
and column c
, look at all cells in row r-1
(columns prev_c
).
(r-1, prev_c)
to (r, c)
is dp[r-1][prev_c] + moveCost[grid[r-1][prev_c]][c] + grid[r][c]
.
prev_c
values and store it in dp[r][c]
.
dp
table, the answer is the minimum value in the last row, since the path can end at any cell in the last row.
This approach ensures we only compute each subproblem once, and we always use the optimal solutions to smaller subproblems to build up to the final answer.
Let's consider the following example:
grid = [[5,3],[4,0],[2,1]]
moveCost = [[9,4],[6,7],[5,8],[7,6],[8,9],[6,7]]
Step 1: Initialize dp[0] = [5, 3]
(the first row of the grid).
Step 2: Compute dp[1]
:
dp[1][0]
(cell value 4):
dp[0][0]
(5): cost = 5 + moveCost[5][0] + 4 = 5 + 6 + 4 = 15dp[0][1]
(3): cost = 3 + moveCost[3][0] + 4 = 3 + 7 + 4 = 14dp[1][0] = 14
dp[1][1]
(cell value 0):
dp[0][0]
(5): cost = 5 + moveCost[5][1] + 0 = 5 + 7 + 0 = 12dp[0][1]
(3): cost = 3 + moveCost[3][1] + 0 = 3 + 6 + 0 = 9dp[1][1] = 9
Step 3: Compute dp[2]
:
dp[2][0]
(cell value 2):
dp[1][0]
(4): cost = 14 + moveCost[4][0] + 2 = 14 + 8 + 2 = 24dp[1][1]
(0): cost = 9 + moveCost[0][0] + 2 = 9 + 9 + 2 = 20dp[2][0] = 20
dp[2][1]
(cell value 1):
dp[1][0]
(4): cost = 14 + moveCost[4][1] + 1 = 14 + 9 + 1 = 24dp[1][1]
(0): cost = 9 + moveCost[0][1] + 1 = 9 + 4 + 1 = 14dp[2][1] = 14
Final step: The answer is min(dp[2][0], dp[2][1]) = min(20, 14) = 14
.
Brute-Force Approach: For each row, for every possible cell, you can go to any cell in the next row. This leads to O(n^m)
possible paths, where n
is the number of columns and m
is the number of rows. This is not feasible for large grids.
Optimized Dynamic Programming Approach:
O(m * n^2)
time complexity.
O(m * n)
, as we need to store the minimum cost for each cell.
The "Minimum Path Cost in a Grid" problem is a classic application of dynamic programming. By breaking the problem into subproblems—computing the minimum cost to reach each cell in each row—and reusing those results, we avoid the combinatorial explosion of brute-force solutions. The key insight is to recognize the dependency of each move on both the value and position of the previous cell, and to systematically build up the solution row by row. This approach is both efficient and elegant, and it demonstrates the power of dynamic programming for grid-based pathfinding problems.