Facebook Pixel

789. Escape The Ghosts

Problem Description

You're playing a PAC-MAN style game on an infinite 2D grid. You start at position [0, 0] and need to reach a target position [x_target, y_target]. There are ghosts on the grid, each starting at their own positions given in the array ghosts, where ghosts[i] = [x_i, y_i] represents the starting position of the i-th ghost.

The game works as follows:

  • Each turn, both you and all ghosts can choose to either:
    • Move 1 unit in any cardinal direction (north, east, south, or west)
    • Stay still
  • All movements happen simultaneously

You successfully escape if you reach the target position before any ghost can reach you. If you and a ghost arrive at the same position (including the target) at the same time, you do not escape.

The problem asks you to determine if it's possible for you to escape regardless of how the ghosts move. Return true if you can guarantee your escape, false otherwise.

The key insight is that since ghosts can move optimally, they will always take the shortest path to intercept you. The solution uses Manhattan distance to check if any ghost can reach the target faster than or at the same time as you. If your Manhattan distance to the target (|target_x| + |target_y|) is less than any ghost's Manhattan distance to the target (|target_x - ghost_x| + |target_y - ghost_y|), then you can escape. Otherwise, a ghost can always intercept you at or before the target.

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

Intuition

At first glance, this problem might seem complex - we need to consider all possible paths we could take and all possible moves the ghosts could make. However, let's think about what the optimal strategy for both parties would be.

Since the ghosts want to catch us, their best strategy is to move directly toward wherever we're trying to go. And since we're trying to reach the target, our best path is to move directly toward the target as well.

Here's the crucial realization: If a ghost can reach the target position in the same or fewer moves than we can, that ghost can always intercept us. Why? Because:

  1. No matter what path we take to the target, we need at least manhattan_distance moves to get there (the Manhattan distance being |target_x| + |target_y| from our starting position [0, 0]).

  2. If a ghost can reach the target in equal or fewer moves, it can simply head straight to the target and wait for us there. Even if we try to take a detour, we'll eventually need to reach the target, and the ghost will be there.

  3. The ghost doesn't need to chase us around the grid - it just needs to get to our destination before or at the same time as us.

Therefore, the problem simplifies to checking: Can we reach the target in fewer moves than any ghost can?

Our distance to target: |target_x - 0| + |target_y - 0| = |target_x| + |target_y|

Each ghost's distance to target: |target_x - ghost_x| + |target_y - ghost_y|

We can escape if and only if our distance is strictly less than every ghost's distance to the target. This is why the solution checks that all ghosts have a greater Manhattan distance to the target than we do.

Learn more about Math patterns.

Solution Approach

The implementation is elegantly simple once we understand the key insight about Manhattan distances:

  1. Extract the target coordinates: We first unpack the target position into tx and ty for easier reference.

  2. Calculate our distance to target: Since we start at [0, 0], our Manhattan distance to the target is simply |tx| + |ty|, which equals abs(tx) + abs(ty).

  3. Check each ghost's distance: For each ghost at position [x, y], we calculate its Manhattan distance to the target as abs(tx - x) + abs(ty - y).

  4. Compare distances: We use Python's all() function to verify that every ghost has a strictly greater distance to the target than we do. The condition abs(tx - x) + abs(ty - y) > abs(tx) + abs(ty) must be true for all ghosts.

The solution uses a generator expression inside all() to efficiently check this condition for each ghost:

all(abs(tx - x) + abs(ty - y) > abs(tx) + abs(ty) for x, y in ghosts)

This returns:

  • True if all ghosts are farther from the target than we are (we can escape)
  • False if at least one ghost can reach the target as fast or faster than us (we cannot escape)

The time complexity is O(n) where n is the number of ghosts, as we need to check each ghost once. The space complexity is O(1) since we only use a constant amount of extra space regardless of the input size.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through a concrete example to see how the Manhattan distance solution works.

Example 1:

  • Target: [2, 3]
  • Ghosts: [[1, 0], [4, 3]]

Step 1: Calculate our distance to the target

  • We start at [0, 0]
  • Our Manhattan distance = |2 - 0| + |3 - 0| = 2 + 3 = 5
  • We need exactly 5 moves to reach the target (for example: right, right, up, up, up)

Step 2: Calculate each ghost's distance to the target

  • Ghost 1 at [1, 0]:

    • Distance = |2 - 1| + |3 - 0| = 1 + 3 = 4
    • This ghost needs only 4 moves to reach the target
  • Ghost 2 at [4, 3]:

    • Distance = |2 - 4| + |3 - 3| = 2 + 0 = 2
    • This ghost needs only 2 moves to reach the target

Step 3: Compare distances

  • Our distance: 5 moves
  • Ghost 1 distance: 4 moves (4 < 5, ghost is closer!)
  • Ghost 2 distance: 2 moves (2 < 5, ghost is closer!)

Since both ghosts can reach the target faster than us, we cannot escape. The function returns False.

Example 2:

  • Target: [1, 1]
  • Ghosts: [[2, 3], [-1, 2]]

Step 1: Calculate our distance

  • Our Manhattan distance = |1| + |1| = 2

Step 2: Calculate ghost distances

  • Ghost 1 at [2, 3]: Distance = |1 - 2| + |1 - 3| = 1 + 2 = 3
  • Ghost 2 at [-1, 2]: Distance = |1 - (-1)| + |1 - 2| = 2 + 1 = 3

Step 3: Compare

  • Our distance: 2 moves
  • Ghost 1: 3 moves (3 > 2, we're closer!)
  • Ghost 2: 3 moves (3 > 2, we're closer!)

Since we can reach the target before any ghost, we can escape. The function returns True.

Solution Implementation

1class Solution:
2    def escapeGhosts(self, ghosts: List[List[int]], target: List[int]) -> bool:
3        """
4        Determines if a player can reach the target before any ghost.
5      
6        The player starts at origin (0, 0) and needs to reach the target.
7        Ghosts can move optimally to intercept the player.
8      
9        Key insight: If any ghost can reach the target in fewer or equal steps
10        than the player, the ghost can intercept by going directly to the target.
11      
12        Args:
13            ghosts: List of ghost positions [x, y]
14            target: Target position [x, y]
15          
16        Returns:
17            True if player can escape to target, False otherwise
18        """
19        # Extract target coordinates
20        target_x, target_y = target
21      
22        # Calculate player's Manhattan distance from origin to target
23        player_distance = abs(target_x) + abs(target_y)
24      
25        # Check if all ghosts need more steps to reach target than the player
26        # For each ghost, calculate Manhattan distance to target
27        for ghost_x, ghost_y in ghosts:
28            ghost_distance = abs(target_x - ghost_x) + abs(target_y - ghost_y)
29          
30            # If any ghost can reach target faster or at same time, player loses
31            if ghost_distance <= player_distance:
32                return False
33      
34        # Player can escape if no ghost can intercept
35        return True
36
1class Solution {
2    /**
3     * Determines if a player can reach the target before any ghost.
4     * The player starts at origin (0, 0) and needs to reach the target position.
5     * Ghosts can intercept if they can reach the target in equal or less moves than the player.
6     * 
7     * @param ghosts 2D array where each element represents a ghost's [x, y] position
8     * @param target Array representing the target position [x, y]
9     * @return true if player can escape (reach target before ghosts), false otherwise
10     */
11    public boolean escapeGhosts(int[][] ghosts, int[] target) {
12        // Extract target coordinates for clarity
13        int targetX = target[0];
14        int targetY = target[1];
15      
16        // Calculate player's Manhattan distance from origin to target
17        int playerDistance = Math.abs(targetX) + Math.abs(targetY);
18      
19        // Check each ghost's distance to the target
20        for (int[] ghost : ghosts) {
21            int ghostX = ghost[0];
22            int ghostY = ghost[1];
23          
24            // Calculate ghost's Manhattan distance to target
25            int ghostDistance = Math.abs(targetX - ghostX) + Math.abs(targetY - ghostY);
26          
27            // If any ghost can reach target faster or at same time as player, escape fails
28            if (ghostDistance <= playerDistance) {
29                return false;
30            }
31        }
32      
33        // Player can reach target before all ghosts
34        return true;
35    }
36}
37
1class Solution {
2public:
3    bool escapeGhosts(vector<vector<int>>& ghosts, vector<int>& target) {
4        // Extract target coordinates for clarity
5        int targetX = target[0];
6        int targetY = target[1];
7      
8        // Calculate player's Manhattan distance from origin (0, 0) to target
9        int playerDistance = abs(targetX) + abs(targetY);
10      
11        // Check if any ghost can reach the target before or at the same time as the player
12        for (const auto& ghost : ghosts) {
13            int ghostX = ghost[0];
14            int ghostY = ghost[1];
15          
16            // Calculate ghost's Manhattan distance to the target
17            int ghostDistance = abs(targetX - ghostX) + abs(targetY - ghostY);
18          
19            // If ghost can reach target before or at the same time as player, escape fails
20            if (ghostDistance <= playerDistance) {
21                return false;
22            }
23        }
24      
25        // Player can reach the target before all ghosts
26        return true;
27    }
28};
29
1/**
2 * Determines if a player can reach the target before any ghost can intercept them.
3 * Uses Manhattan distance to calculate the shortest path for both player and ghosts.
4 * 
5 * @param ghosts - Array of ghost positions, where each ghost is represented as [x, y] coordinates
6 * @param target - Target position as [x, y] coordinates
7 * @returns true if player can reach target safely, false if any ghost can intercept
8 */
9function escapeGhosts(ghosts: number[][], target: number[]): boolean {
10    // Destructure target coordinates for clarity
11    const [targetX, targetY] = target;
12  
13    // Calculate player's distance to target (starting from origin [0, 0])
14    const playerDistanceToTarget: number = Math.abs(targetX) + Math.abs(targetY);
15  
16    // Check if any ghost can reach the target before or at the same time as the player
17    for (const ghost of ghosts) {
18        const [ghostX, ghostY] = ghost;
19      
20        // Calculate Manhattan distance from current ghost to target
21        const ghostDistanceToTarget: number = Math.abs(targetX - ghostX) + Math.abs(targetY - ghostY);
22      
23        // If ghost can reach target before or at same time as player, escape is impossible
24        if (ghostDistanceToTarget <= playerDistanceToTarget) {
25            return false;
26        }
27    }
28  
29    // All ghosts are too far to intercept, player can escape successfully
30    return true;
31}
32

Time and Space Complexity

Time Complexity: O(n) where n is the number of ghosts in the input list. The algorithm iterates through each ghost exactly once to calculate the Manhattan distance from that ghost to the target and compare it with the player's Manhattan distance to the target. Each distance calculation takes O(1) time (simple arithmetic operations), so the overall time complexity is linear with respect to the number of ghosts.

Space Complexity: O(1). The algorithm only uses a constant amount of extra space regardless of the input size. The variables tx and ty store the target coordinates, and the generator expression in the all() function processes one ghost at a time without storing intermediate results. No additional data structures are created that scale with the input size.

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

Common Pitfalls

1. Overthinking Movement Strategies

Many developers initially try to simulate actual movement paths or implement complex pathfinding algorithms like BFS/DFS to track all possible moves. This leads to unnecessarily complex solutions that consider intermediate positions and collision scenarios.

Why it's wrong: The problem seems to require tracking movements, but the key insight is that both the player and ghosts will take optimal paths. Since the grid is infinite and obstacle-free, the optimal path is always the direct Manhattan path to the target.

Correct approach: Simply compare Manhattan distances. If a ghost can reach the target in equal or fewer steps, it will go directly there and wait, guaranteeing an interception.

2. Misunderstanding Simultaneous Movement

Some solutions incorrectly assume that the player moves first, then ghosts move, or vice versa. This can lead to checking if the player can "outrun" ghosts along the path.

Why it's wrong: All entities move simultaneously each turn. This means we can't gain an advantage by moving first or analyzing turn-by-turn positions.

Correct approach: Focus only on the final destination. Since movements are simultaneous, what matters is who can reach the target first, not the intermediate positions.

3. Incorrect Distance Calculation

A common error is using Euclidean distance sqrt((x2-x1)² + (y2-y1)²) instead of Manhattan distance, or forgetting to use absolute values.

Wrong implementation:

# Incorrect - using Euclidean distance
player_distance = math.sqrt(target_x**2 + target_y**2)

# Incorrect - missing absolute values
player_distance = target_x + target_y

Correct implementation:

player_distance = abs(target_x) + abs(target_y)
ghost_distance = abs(target_x - ghost_x) + abs(target_y - ghost_y)

4. Off-by-One Error in Comparison

Using < instead of <= when comparing distances, thinking the player wins ties.

Wrong:

if ghost_distance < player_distance:  # Wrong - should be <=
    return False

Correct:

if ghost_distance <= player_distance:  # Ghost wins on ties
    return False

The problem explicitly states: "If you and a ghost arrive at the same position at the same time, you do not escape."

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

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

Load More