913. Cat and Mouse
Description
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, or0
if the game is a draw.
Example 1:
Input: graph = [[2,5],[3],[0,4,5],[1,4,5],[2,3],[0,2,3]] Output: 0
Example 2:
Input: graph = [[1,3],[0],[3],[0,2]] Output: 1
Constraints:
3 <= graph.length <= 50
1 <= graph[i].length < graph.length
0 <= graph[i][j] < graph.length
graph[i][j] != i
graph[i]
is unique.- The mouse and the cat can always move.
Solutions
Solution: Breadth-First Search
- Time complexity: O(n3)
- Space complexity: O(n2)
JavaScript
js
/**
* @param {number[][]} graph
* @return {number}
*/
const catMouseGame = function (graph) {
const DRAW = 0;
const MOUSE_WIN = 1;
const CAT_WIN = 2;
const MOUSE_START = 1;
const CAT_START = 2;
const MOUSE_ROUND = 0;
const CAT_ROUND = 1;
const n = graph.length;
const defaultState = { [MOUSE_ROUND]: DRAW, [CAT_ROUND]: DRAW };
const defaultDegree = { [MOUSE_ROUND]: 0, [CAT_ROUND]: 0 };
const states = new Array(n)
.fill('')
.map(_ =>
new Array(n)
.fill('')
.map(_ => ({ ...defaultState })),
);
const outDegree = new Array(n)
.fill('')
.map(_ =>
new Array(n)
.fill('')
.map(_ => ({ ...defaultDegree })),
);
let queue = [];
for (let mouse = 0; mouse < n; mouse++) {
for (let cat = 0; cat < n; cat++) {
outDegree[mouse][cat][MOUSE_ROUND] = graph[mouse].length;
outDegree[mouse][cat][CAT_ROUND] = graph[cat].filter(node => node).length;
}
}
for (let cat = 1; cat < n; cat++) {
states[0][cat][MOUSE_ROUND] = MOUSE_WIN;
states[0][cat][CAT_ROUND] = MOUSE_WIN;
states[cat][cat][MOUSE_ROUND] = CAT_WIN;
states[cat][cat][CAT_ROUND] = CAT_WIN;
queue.push({ mouse: 0, cat, round: MOUSE_ROUND, state: MOUSE_WIN }, { mouse: 0, cat, round: CAT_ROUND, state: MOUSE_WIN }, { mouse: cat, cat, round: MOUSE_ROUND, state: CAT_WIN }, { mouse: cat, cat, round: CAT_ROUND, state: CAT_WIN });
}
while (queue.length) {
const nextQueue = [];
for (const { mouse, cat, round, state } of queue) {
if (mouse === MOUSE_START && cat === CAT_START && round === MOUSE_ROUND) {
return state;
}
const prevRound = round === MOUSE_ROUND ? CAT_ROUND : MOUSE_ROUND;
const isPrevMouseRound = prevRound === MOUSE_ROUND;
const prevNodes = graph[isPrevMouseRound ? mouse : cat];
for (const prevNode of prevNodes) {
const prevCat = isPrevMouseRound ? cat : prevNode;
if (prevCat === 0) continue;
const prevMouse = isPrevMouseRound ? prevNode : mouse;
if (states[prevMouse][prevCat][prevRound] !== DRAW) continue;
const isMouseWin = isPrevMouseRound && state === MOUSE_WIN;
const isCatWin = !isPrevMouseRound && state === CAT_WIN;
if (isMouseWin || isCatWin || --outDegree[prevMouse][prevCat][prevRound] === 0) {
states[prevMouse][prevCat][prevRound] = state;
nextQueue.push({ mouse: prevMouse, cat: prevCat, round: prevRound, state });
}
}
}
queue = nextQueue;
}
return states[MOUSE_START][CAT_START][MOUSE_ROUND];
};