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:
- 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.
- The mouse moves second and can also move one cell at a time in any of the four directions except for cells with walls.
- 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.
- 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:
- 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.
- 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.
- 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