Similar Problems

Similar Problems not available

Cat And Mouse - Leetcode Solution

Companies:

LeetCode:  Cat And Mouse Leetcode Solution

Difficulty: Hard

Topics: math dynamic-programming graph  

The Cat and Mouse problem on LeetCode is a classic graph theory problem where a cat is trying to catch a mouse in a maze.

The problem statement can be summarized as follows:

Given a maze represented by a 2D matrix, where 0 represents an empty cell, 1 represents a wall and 2 represents the mouse's position, and 3 represents the cat's position. The cat and mouse play the following game:

  1. The cat moves first and can move one cell at a time in any of the four directions (up, down, left, right) except for cells with walls.
  2. The mouse moves second and can also move one cell at a time in any of the four directions except for cells with walls.
  3. The cat wins if it can catch the mouse before the mouse reaches the border of the maze. The mouse wins if it can reach the border before being caught by the cat.
  4. Return 1 if the mouse can escape, and 0 otherwise.

To solve this problem, we need to first identify the different states and define a graph that captures the transitions between these states. In this case, a state can be represented by the position of the cat, the position of the mouse, and the turn or move number.

Once we have identified the states and defined the graph, we can use breadth-first search (BFS) to find the shortest path from the starting state (cat's position, mouse's position, move number) to the goal state (mouse reaches the border or cat catches the mouse).

Here's the step-by-step solution to the Cat and Mouse problem on LeetCode:

  1. Identify the states and define the graph:

A state can be defined by the position of the cat, the position of the mouse, and the turn or move number. We need to maintain two separate graphs, one for the cat's moves and another for the mouse's moves.

For the cat's moves, we can create a graph where each node represents a state, and the edges connect nodes that are reachable from each other in one move. A node can be represented by a tuple (cat_x, cat_y, mouse_x, mouse_y, move_number, player), where player is 0 for the cat and 1 for the mouse. The edges can be found by iterating over the four possible moves of the cat and checking if it's a valid move (the cell is not a wall and doesn't exceed the maze boundaries).

The mouse's graph is similar, but we need to take into account that the mouse cannot move to the cell where the cat is currently standing. To handle this, we can use a set(AVOID) to store all the cells that the mouse cannot move to.

  1. Use BFS to find the shortest path:

We need to apply BFS separately on the cat's and mouse's graphs. The BFS algorithm starts at the starting state (cat's position, mouse's position, move number = 0, player = 0), enqueues it into the queue, and marks it as visited. We continue with a standard BFS loop where we dequeue the next state, generate its neighbors using the graph, and enqueue them if they haven't been visited before. We terminate the loop when we find the goal state (mouse reaches the border or cat catches the mouse) or when the queue is empty.

  1. Return the result:

If the BFS algorithm terminates because the goal state is found, return 1 (mouse can escape), otherwise return 0 (cat catches the mouse).

Here is the Python code implementation of the above solution:

from collections import deque

def canEscape(maze):
    # find the positions of the cat and mouse in the maze
    rows, cols = len(maze), len(maze[0])
    cat_pos, mouse_pos = None, None
    for i in range(rows):
        for j in range(cols):
            if maze[i][j] == 3:
                cat_pos = (i, j)
            elif maze[i][j] == 2:
                mouse_pos = (i, j)
    # define the four possible moves for a cell
    moves = [(1, 0), (-1, 0), (0, 1), (0, -1)]
    # define a set of cells to avoid for the mouse
    AVOID = set()
    # create the graphs for the cat and mouse moves
    def createGraph(player):
        graph = {}
        queue = deque([(cat_pos, mouse_pos)])
        move_number = 0
        while queue:
            for _ in range(len(queue)):
                cat, mouse = queue.popleft()
                if player == 1 and (mouse[0], mouse[1], cat[0], cat[1]) in graph:
                    continue # avoid cycles if mouse's turn
                state = (*cat, *mouse, move_number, player)
                graph[state] = []
                for move in moves:
                    new_cat = (cat[0]+move[0], cat[1]+move[1])
                    if 0 <= new_cat[0] < rows and 0 <= new_cat[1] < cols and maze[new_cat[0]][new_cat[1]] != 1:
                        new_mouse = (mouse[0]+move[0], mouse[1]+move[1])
                        if 0 <= new_mouse[0] < rows and 0 <= new_mouse[1] < cols and maze[new_mouse[0]][new_mouse[1]] != 1:
                            if player == 0 or new_mouse not in AVOID:
                                neighbor = (*new_cat, *new_mouse, move_number+1, 1-player)
                                graph[state].append(neighbor)
                                if neighbor not in graph:
                                    queue.append((new_cat, new_mouse))
            move_number += 1
        return graph
    cat_move_graph = createGraph(0)
    mouse_move_graph = createGraph(1)
    # run BFS on the cat and mouse graphs separately
    visited = set()
    queue = deque([(cat_pos, mouse_pos, 0, 0)])
    while queue:
        for _ in range(len(queue)):
            cat, mouse, move_number, player = queue.popleft()
            if (cat, mouse, player) in visited:
                continue
            visited.add((cat, mouse, player))
            state = (*cat, *mouse, move_number, player)
            if player == 1 and (cat[0], cat[1], mouse[0], mouse[1]) in cat_move_graph:
                # if it's the mouse's turn and the cat is already caught up with the mouse
                for neighbor in cat_move_graph[(cat[0], cat[1], mouse[0], mouse[1], move_number, 0)]:
                    if neighbor[-1] == 0:
                        # if the cat moves to the mouse position, it catches the mouse
                        return 0
            elif player == 0 and (mouse[0], mouse[1], cat[0], cat[1]) in mouse_move_graph:
                # if it's the cat's turn and the mouse is already at a position visited by the mouse earlier
                for neighbor in mouse_move_graph[(mouse[0], mouse[1], cat[0], cat[1], move_number, 1)]:
                    if neighbor[-1] == 1 and neighbor[:-1] not in visited:
                        # if the mouse can still move to a new position, it hasn't escaped yet
                        queue.append((*neighbor[:-1], move_number+1, 1-player))
            elif (cat, mouse) in [(0,j) for j in range(cols)] or (cat, mouse) in [(rows-1,j) for j in range(cols)] or (cat, mouse) in [(i,0) for i in range(rows)] or (cat, mouse) in [(i,cols-1) for i in range(rows)]:
                # if mouse has reached the border
                return 1
            else:
                # if the BFS hasn't terminated yet, enqueue the neighbors
                neighbors = cat_move_graph[state] if player == 0 else mouse_move_graph[state]
                for neighbor in neighbors:
                    if neighbor not in visited:
                        queue.append((*neighbor[:-2], move_number+1, 1-player))
    # if the BFS has terminated but the goal state isn't reached, return 0
    return 0

The time complexity of this algorithm is O(R^2C^2M^2), where R is the number of rows, C is the number of columns, and M is the maximum number of moves required to reach the goal state. The space complexity is O(R^2C^2M^2) for the visited set and the two graphs.

Cat And Mouse Solution Code

1