Facebook Pixel

2087. Minimum Cost Homecoming of a Robot in a Grid

Problem Description

You have a robot on an m x n grid that needs to find its way home. The grid uses coordinates where (0, 0) is the top-left cell and (m - 1, n - 1) is the bottom-right cell.

The robot starts at position startPos = [start_row, start_col] and needs to reach its home at position homePos = [home_row, home_col].

Movement Rules:

  • The robot can move one cell at a time in four directions: left, right, up, or down
  • The robot cannot move outside the grid boundaries

Cost System:

  • Each move has a cost based on the destination cell
  • When moving up or down into a cell in row r, the cost is rowCosts[r]
  • When moving left or right into a cell in column c, the cost is colCosts[c]

You're given:

  • startPos: The robot's starting position [start_row, start_col]
  • homePos: The robot's home position [home_row, home_col]
  • rowCosts: An array of length m where rowCosts[r] is the cost of entering row r
  • colCosts: An array of length n where colCosts[c] is the cost of entering column c

Your task is to find the minimum total cost for the robot to travel from its starting position to its home.

The key insight is that regardless of the path taken, the robot must eventually move through all the rows between start and home (paying their row costs) and all the columns between start and home (paying their column costs). Since we pay based on the destination cell, the optimal path is simply the direct path - moving straight through all necessary rows and columns. The solution sums up the costs of all rows and columns the robot must enter to reach home, excluding the starting position since we begin there.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

At first glance, this might seem like a shortest path problem requiring algorithms like BFS or Dijkstra. However, there's a crucial observation that simplifies everything: the cost structure makes every path equally optimal.

Let's think about why this is true. When we move from startPos to homePos, no matter which path we take:

  • We must eventually move through every row between the starting row and home row
  • We must eventually move through every column between the starting column and home column

The key insight is in how costs are calculated - we pay based on the destination cell we're entering, not the path we take to get there.

For example, if we need to go from row 2 to row 5:

  • Whether we go 2β†’3β†’4β†’5 directly, or take a detour like 2β†’3β†’2β†’3β†’4β†’5β†’4β†’5, we still must enter rows 3, 4, and 5 at least once
  • Each row cost rowCosts[r] is paid when entering row r, regardless of how we got there

Since we must visit all intermediate rows and columns at least once, and revisiting them only adds extra cost, the minimum cost is achieved by visiting each required row and column exactly once. This means any path that doesn't backtrack is optimal.

Therefore, the total minimum cost is simply:

  • Sum of rowCosts for all rows we need to enter (between start row and home row, exclusive of start)
  • Plus sum of colCosts for all columns we need to enter (between start column and home column, exclusive of start)

We don't include the starting position's cost because we begin there - we only pay for cells we move into, not where we start from.

This transforms what appears to be a complex pathfinding problem into a simple arithmetic calculation: just sum up the costs of all rows and columns we must traverse.

Learn more about Greedy patterns.

Solution Approach

The implementation is straightforward once we understand that we simply need to sum the costs of all rows and columns the robot must enter.

Let's break down the solution step by step:

  1. Extract starting and ending positions:

    i, j = startPos  # starting row and column
    x, y = homePos   # destination row and column
  2. Calculate row movement costs:

    • If the robot needs to move down (i < x), we sum the costs from row i+1 to row x (inclusive)
    • If the robot needs to move up (i > x), we sum the costs from row x to row i-1 (inclusive)
    if i < x:
        ans += sum(rowCosts[i + 1 : x + 1])  # moving down
    else:
        ans += sum(rowCosts[x:i])            # moving up
  3. Calculate column movement costs:

    • If the robot needs to move right (j < y), we sum the costs from column j+1 to column y (inclusive)
    • If the robot needs to move left (j > y), we sum the costs from column y to column j-1 (inclusive)
    if j < y:
        ans += sum(colCosts[j + 1 : y + 1])  # moving right
    else:
        ans += sum(colCosts[y:j])            # moving left

Key Implementation Details:

  • Python slicing notation: array[start:end] includes elements from index start up to but not including index end
  • For downward/rightward movement: We use [i+1 : x+1] and [j+1 : y+1] to include the destination but exclude the starting position
  • For upward/leftward movement: We use [x:i] and [y:j] which naturally excludes the starting position and includes all cells we must enter

Time Complexity: O(m + n) where m is the number of rows and n is the number of columns, as we might need to sum through all rows and columns in the worst case.

Space Complexity: O(1) as we only use a constant amount of extra space to store the answer and indices.

The elegance of this solution lies in recognizing that the problem's cost structure allows us to avoid complex pathfinding algorithms and instead use simple arithmetic summation.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to see how this solution works.

Example Setup:

  • Grid: 3Γ—4 (3 rows, 4 columns)
  • startPos = [1, 0] (row 1, column 0)
  • homePos = [2, 3] (row 2, column 3)
  • rowCosts = [5, 1, 3]
  • colCosts = [6, 4, 2, 1]

Visualizing the Grid:

     col0  col1  col2  col3
row0  [ ]   [ ]   [ ]   [ ]
row1  [S]   [ ]   [ ]   [ ]    <- Start at (1,0)
row2  [ ]   [ ]   [ ]   [H]    <- Home at (2,3)

Step 1: Identify Movement Requirements

  • Row movement: From row 1 to row 2 (moving down)
  • Column movement: From column 0 to column 3 (moving right)

Step 2: Calculate Row Costs Since we're moving from row 1 to row 2 (i < x):

  • We need to enter row 2
  • Cost = rowCosts[2] = 3
  • Using the formula: sum(rowCosts[1+1 : 2+1]) = sum(rowCosts[2:3]) = rowCosts[2] = 3

Step 3: Calculate Column Costs Since we're moving from column 0 to column 3 (j < y):

  • We need to enter columns 1, 2, and 3
  • Costs = colCosts[1] + colCosts[2] + colCosts[3] = 4 + 2 + 1 = 7
  • Using the formula: sum(colCosts[0+1 : 3+1]) = sum(colCosts[1:4]) = 4 + 2 + 1 = 7

Step 4: Total Cost Total minimum cost = Row costs + Column costs = 3 + 7 = 10

Verification with Different Paths: Let's verify this is indeed minimum by checking two different paths:

Path 1: Down first, then right

  • (1,0) β†’ (2,0): Enter row 2, cost = 3
  • (2,0) β†’ (2,1): Enter column 1, cost = 4
  • (2,1) β†’ (2,2): Enter column 2, cost = 2
  • (2,2) β†’ (2,3): Enter column 3, cost = 1
  • Total: 3 + 4 + 2 + 1 = 10 βœ“

Path 2: Right first, then down

  • (1,0) β†’ (1,1): Enter column 1, cost = 4
  • (1,1) β†’ (1,2): Enter column 2, cost = 2
  • (1,2) β†’ (1,3): Enter column 3, cost = 1
  • (1,3) β†’ (2,3): Enter row 2, cost = 3
  • Total: 4 + 2 + 1 + 3 = 10 βœ“

Both paths yield the same cost of 10, confirming that any non-backtracking path gives the optimal result!

Solution Implementation

1class Solution:
2    def minCost(
3        self,
4        startPos: List[int],
5        homePos: List[int],
6        rowCosts: List[int],
7        colCosts: List[int],
8    ) -> int:
9        """
10        Calculate the minimum cost to move from start position to home position in a grid.
11      
12        Args:
13            startPos: Starting position [row, column]
14            homePos: Target home position [row, column]
15            rowCosts: Cost to enter each row
16            colCosts: Cost to enter each column
17          
18        Returns:
19            Minimum total cost to reach home from start position
20        """
21        # Extract starting row and column indices
22        start_row, start_col = startPos
23        # Extract target home row and column indices
24        home_row, home_col = homePos
25      
26        # Initialize total cost accumulator
27        total_cost = 0
28      
29        # Calculate row traversal costs
30        if start_row < home_row:
31            # Moving down: sum costs of rows from (start_row + 1) to home_row (inclusive)
32            total_cost += sum(rowCosts[start_row + 1 : home_row + 1])
33        else:
34            # Moving up: sum costs of rows from home_row to (start_row - 1) (inclusive)
35            total_cost += sum(rowCosts[home_row : start_row])
36      
37        # Calculate column traversal costs
38        if start_col < home_col:
39            # Moving right: sum costs of columns from (start_col + 1) to home_col (inclusive)
40            total_cost += sum(colCosts[start_col + 1 : home_col + 1])
41        else:
42            # Moving left: sum costs of columns from home_col to (start_col - 1) (inclusive)
43            total_cost += sum(colCosts[home_col : start_col])
44      
45        return total_cost
46
1class Solution {
2    public int minCost(int[] startPos, int[] homePos, int[] rowCosts, int[] colCosts) {
3        // Extract starting position coordinates
4        int startRow = startPos[0];
5        int startCol = startPos[1];
6      
7        // Extract home position coordinates
8        int homeRow = homePos[0];
9        int homeCol = homePos[1];
10      
11        // Initialize total cost
12        int totalCost = 0;
13      
14        // Calculate row movement costs
15        if (startRow < homeRow) {
16            // Moving down: sum costs from (startRow + 1) to homeRow
17            for (int row = startRow + 1; row <= homeRow; row++) {
18                totalCost += rowCosts[row];
19            }
20        } else {
21            // Moving up: sum costs from homeRow to (startRow - 1)
22            for (int row = homeRow; row < startRow; row++) {
23                totalCost += rowCosts[row];
24            }
25        }
26      
27        // Calculate column movement costs
28        if (startCol < homeCol) {
29            // Moving right: sum costs from (startCol + 1) to homeCol
30            for (int col = startCol + 1; col <= homeCol; col++) {
31                totalCost += colCosts[col];
32            }
33        } else {
34            // Moving left: sum costs from homeCol to (startCol - 1)
35            for (int col = homeCol; col < startCol; col++) {
36                totalCost += colCosts[col];
37            }
38        }
39      
40        return totalCost;
41    }
42}
43
1class Solution {
2public:
3    int minCost(vector<int>& startPos, vector<int>& homePos, vector<int>& rowCosts, vector<int>& colCosts) {
4        // Extract starting position coordinates
5        int startRow = startPos[0];
6        int startCol = startPos[1];
7      
8        // Extract destination (home) position coordinates
9        int homeRow = homePos[0];
10        int homeCol = homePos[1];
11      
12        // Initialize total cost
13        int totalCost = 0;
14      
15        // Calculate row movement cost
16        // If moving down (startRow < homeRow), sum costs of rows from startRow+1 to homeRow
17        if (startRow < homeRow) {
18            totalCost += accumulate(rowCosts.begin() + startRow + 1, 
19                                   rowCosts.begin() + homeRow + 1, 
20                                   0);
21        } 
22        // If moving up (startRow > homeRow), sum costs of rows from homeRow to startRow-1
23        else if (startRow > homeRow) {
24            totalCost += accumulate(rowCosts.begin() + homeRow, 
25                                   rowCosts.begin() + startRow, 
26                                   0);
27        }
28      
29        // Calculate column movement cost
30        // If moving right (startCol < homeCol), sum costs of columns from startCol+1 to homeCol
31        if (startCol < homeCol) {
32            totalCost += accumulate(colCosts.begin() + startCol + 1, 
33                                   colCosts.begin() + homeCol + 1, 
34                                   0);
35        } 
36        // If moving left (startCol > homeCol), sum costs of columns from homeCol to startCol-1
37        else if (startCol > homeCol) {
38            totalCost += accumulate(colCosts.begin() + homeCol, 
39                                   colCosts.begin() + startCol, 
40                                   0);
41        }
42      
43        return totalCost;
44    }
45};
46
1function minCost(startPos: number[], homePos: number[], rowCosts: number[], colCosts: number[]): number {
2    // Extract starting position coordinates
3    const startRow: number = startPos[0];
4    const startCol: number = startPos[1];
5  
6    // Extract destination (home) position coordinates
7    const homeRow: number = homePos[0];
8    const homeCol: number = homePos[1];
9  
10    // Initialize total cost
11    let totalCost: number = 0;
12  
13    // Calculate row movement cost
14    // If moving down (startRow < homeRow), sum costs of rows from startRow+1 to homeRow
15    if (startRow < homeRow) {
16        for (let i = startRow + 1; i <= homeRow; i++) {
17            totalCost += rowCosts[i];
18        }
19    } 
20    // If moving up (startRow > homeRow), sum costs of rows from homeRow to startRow-1
21    else if (startRow > homeRow) {
22        for (let i = homeRow; i < startRow; i++) {
23            totalCost += rowCosts[i];
24        }
25    }
26  
27    // Calculate column movement cost
28    // If moving right (startCol < homeCol), sum costs of columns from startCol+1 to homeCol
29    if (startCol < homeCol) {
30        for (let i = startCol + 1; i <= homeCol; i++) {
31            totalCost += colCosts[i];
32        }
33    } 
34    // If moving left (startCol > homeCol), sum costs of columns from homeCol to startCol-1
35    else if (startCol > homeCol) {
36        for (let i = homeCol; i < startCol; i++) {
37            totalCost += colCosts[i];
38        }
39    }
40  
41    return totalCost;
42}
43

Time and Space Complexity

Time Complexity: O(m + n) where m is the number of rows between start and home positions, and n is the number of columns between start and home positions.

  • The algorithm calculates the sum of row costs between the starting row and home row, which takes O(m) time in the worst case when traversing all rows.
  • Similarly, it calculates the sum of column costs between the starting column and home column, which takes O(n) time in the worst case when traversing all columns.
  • Both sum operations use Python's built-in sum() function on array slices, which iterates through the elements once.
  • The total time complexity is O(m + n), which in the worst case (when start and home are at opposite corners of the grid) becomes O(rows + cols) where rows and cols are the total dimensions of the grid.

Space Complexity: O(1)

  • The algorithm only uses a constant amount of extra space for variables i, j, x, y, and ans.
  • The array slicing operations in Python (rowCosts[i+1:x+1] and colCosts[j+1:y+1]) create temporary views/iterators that are consumed by the sum() function, but don't create full copies of the arrays.
  • No additional data structures are created that scale with input size.
  • Therefore, the space complexity is constant O(1).

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Misunderstanding the Cost Model

A common mistake is thinking that the cost is associated with the current cell or the movement action itself, rather than the destination cell. This misunderstanding can lead to:

  • Including the starting position's cost in the total
  • Attempting to use complex pathfinding algorithms like Dijkstra or dynamic programming

Wrong approach example:

# INCORRECT: Including starting position cost
total_cost = rowCosts[start_row] + colCosts[start_col]  # Wrong!

Solution: Remember that costs are only incurred when entering a cell, not when starting from it. The starting position has no associated cost.

2. Off-by-One Errors in Array Slicing

Python's slice notation [start:end] excludes the end index, which can cause confusion when calculating inclusive ranges.

Common mistakes:

# INCORRECT: Missing the destination cell
if start_row < home_row:
    total_cost += sum(rowCosts[start_row + 1 : home_row])  # Missing home_row!

# INCORRECT: Including the starting position
if start_row > home_row:
    total_cost += sum(rowCosts[home_row : start_row + 1])  # Includes start_row!

Solution: Carefully verify slice boundaries:

  • When moving forward (down/right): Use [start + 1 : end + 1] to exclude start and include end
  • When moving backward (up/left): Use [end : start] which naturally excludes start and includes end

3. Overthinking the Path Optimization

Developers often assume they need to find the "shortest path" or use graph algorithms, leading to unnecessarily complex solutions with BFS, DFS, or dynamic programming.

Wrong approach:

# UNNECESSARY: Using BFS to find minimum cost path
from collections import deque
def minCost_overcomplicated(self, startPos, homePos, rowCosts, colCosts):
    queue = deque([(startPos[0], startPos[1], 0)])
    visited = set()
    # ... complex BFS implementation ...

Solution: Recognize that any path requires traversing the same rows and columns exactly once. The total cost is always the sum of costs for all rows and columns between start and home, regardless of the order of moves.

4. Incorrect Handling of Same Row/Column Cases

Forgetting to handle cases where the robot doesn't need to change rows or columns.

Potential issue:

# This code actually handles it correctly due to Python's slice behavior,
# but it's important to verify:
if start_row == home_row:
    # rowCosts[start_row + 1 : start_row + 1] returns empty list
    # sum([]) = 0, which is correct

Solution: The current implementation handles this correctly because summing an empty slice returns 0. However, you could add explicit checks for clarity:

if start_row != home_row:
    if start_row < home_row:
        total_cost += sum(rowCosts[start_row + 1 : home_row + 1])
    else:
        total_cost += sum(rowCosts[home_row : start_row])
Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of these pictures shows the visit order of a depth-first search?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More