Skip to main content
Back to problems
#913
Hard Algorithms

Cat and mouse

Math Dynamic Programming Graph Theory Topological Sort Memoization Game Theory
34.8% acceptance
Feb 25, 2026
1011
179
A game on an undirected graph is played by two players, Mouse and Cat, who alternate turns. The graph is given as follows: graph[a] is a list of all nodes b such that ab is an edge of the graph. The mouse starts at node 1 and goes first, the cat starts at node 2 and goes second, and there is a hole at node 0. During each player's turn, they must travel along one edge of the graph that meets where they are. For example, if the Mouse is at node 1, it must travel to any node in graph[1]. Additionally, it is not allowed for the Cat to travel to the Hole (node 0). Then, the game can end in three ways: If ever the Cat occupies the same node as the Mouse, the Cat wins. If ever the Mouse reaches the Hole, the Mouse wins. If ever a position is repeated (i.e., the players are in the same position as a previous turn, and it is the same player's turn to move), the game is a draw. Given a graph, and assuming both players play optimally, return 1 if the mouse wins the game, 2 if the cat wins the game, or 0 if the game is a draw.

Solution

Rust
Time O(n * m)
Space O(n * m)
LeetCode
solution.rs
impl Solution {
  pub fn cat_mouse_game(graph: Vec<Vec<i32>>) -> i32 {
    let n = graph.len();
    const DRAW: i32 = 0; const MOUSE: i32 = 1; const CAT: i32 = 2;
    // color[mouse][cat][turn] = outcome
    let mut color = vec![vec![vec![DRAW; 2]; n]; n];
    // degree[mouse][cat][turn] = number of moves not yet determined
    let mut degree = vec![vec![vec![0usize; 2]; n]; n];
    for m in 0..n {
      for c in 0..n {
        degree[m][c][0] = graph[m].len();
        degree[m][c][1] = graph[c].iter().filter(|&&x| x != 0).count();
      }
    }
    let mut queue = std::collections::VecDeque::new();
    for i in 0..n {
      for t in 0..2usize {
        if color[0][i][t] == DRAW {
          color[0][i][t] = MOUSE;
          queue.push_back((0usize, i, t, MOUSE));
        }
        if i != 0 && color[i][i][t] == DRAW {
          color[i][i][t] = CAT;
          queue.push_back((i, i, t, CAT));
        }
      }
    }
    while let Some((m, c, t, res)) = queue.pop_front() {
      let prev_t = 1 - t;
      if prev_t == 0 {
        // prev turn was mouse's turn: mouse moved to m, so look at prev mouse positions
        for &pm in &graph[m] {
          let pm = pm as usize;
          if color[pm][c][0] != DRAW { continue; }
          if res == MOUSE {
            color[pm][c][0] = MOUSE;
            queue.push_back((pm, c, 0, MOUSE));
          } else {
            if degree[pm][c][0] > 0 { degree[pm][c][0] -= 1; }
            if degree[pm][c][0] == 0 {
              color[pm][c][0] = res;
              queue.push_back((pm, c, 0, res));
            }
          }
        }
      } else {
        // prev turn was cat's: cat moved to c, look at prev cat positions
        for &pc in &graph[c] {
          let pc = pc as usize;
          if pc == 0 { continue; } // cat can't be at hole
          if color[m][pc][1] != DRAW { continue; }
          if res == CAT {
            color[m][pc][1] = CAT;
            queue.push_back((m, pc, 1, CAT));
          } else {
            if degree[m][pc][1] > 0 { degree[m][pc][1] -= 1; }
            if degree[m][pc][1] == 0 {
              color[m][pc][1] = res;
              queue.push_back((m, pc, 1, res));
            }
          }
        }
      }
    }
    color[1][2][0]
  }
}