|
| 1 | +/** |
| 2 | + * 1728. Cat and Mouse II |
| 3 | + * https://leetcode.com/problems/cat-and-mouse-ii/ |
| 4 | + * Difficulty: Hard |
| 5 | + * |
| 6 | + * A game is played by a cat and a mouse named Cat and Mouse. |
| 7 | + * |
| 8 | + * The environment is represented by a grid of size rows x cols, where each element is a wall, |
| 9 | + * floor, player (Cat, Mouse), or food. |
| 10 | + * - Players are represented by the characters 'C'(Cat),'M'(Mouse). |
| 11 | + * - Floors are represented by the character '.' and can be walked on. |
| 12 | + * - Walls are represented by the character '#' and cannot be walked on. |
| 13 | + * - Food is represented by the character 'F' and can be walked on. |
| 14 | + * - There is only one of each character 'C', 'M', and 'F' in grid. |
| 15 | + * |
| 16 | + * Mouse and Cat play according to the following rules: |
| 17 | + * - Mouse moves first, then they take turns to move. |
| 18 | + * - During each turn, Cat and Mouse can jump in one of the four directions (left, right, up, down). |
| 19 | + * They cannot jump over the wall nor outside of the grid. |
| 20 | + * - catJump, mouseJump are the maximum lengths Cat and Mouse can jump at a time, respectively. |
| 21 | + * Cat and Mouse can jump less than the maximum length. |
| 22 | + * - Staying in the same position is allowed. |
| 23 | + * - Mouse can jump over Cat. |
| 24 | + * |
| 25 | + * The game can end in 4 ways: |
| 26 | + * - If Cat occupies the same position as Mouse, Cat wins. |
| 27 | + * - If Cat reaches the food first, Cat wins. |
| 28 | + * - If Mouse reaches the food first, Mouse wins. |
| 29 | + * - If Mouse cannot get to the food within 1000 turns, Cat wins. |
| 30 | + * |
| 31 | + * Given a rows x cols matrix grid and two integers catJump and mouseJump, return true if Mouse |
| 32 | + * can win the game if both Cat and Mouse play optimally, otherwise return false. |
| 33 | + */ |
| 34 | + |
| 35 | +/** |
| 36 | + * @param {string[]} grid |
| 37 | + * @param {number} catJump |
| 38 | + * @param {number} mouseJump |
| 39 | + * @return {boolean} |
| 40 | + */ |
| 41 | +var canMouseWin = function(grid, catJump, mouseJump) { |
| 42 | + if (typeof grid === 'string') grid = [grid]; |
| 43 | + const rows = grid.length; |
| 44 | + const cols = grid[0].length; |
| 45 | + const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]]; |
| 46 | + let mouse; |
| 47 | + let cat; |
| 48 | + let food; |
| 49 | + let available = 0; |
| 50 | + |
| 51 | + for (let i = 0; i < rows; i++) { |
| 52 | + for (let j = 0; j < cols; j++) { |
| 53 | + const cell = grid[i][j]; |
| 54 | + if (cell !== '#') available++; |
| 55 | + if (cell === 'M') mouse = [i, j]; |
| 56 | + else if (cell === 'C') cat = [i, j]; |
| 57 | + else if (cell === 'F') food = [i, j]; |
| 58 | + } |
| 59 | + } |
| 60 | + |
| 61 | + const memo = new Map(); |
| 62 | + |
| 63 | + return canWin(0, mouse, cat); |
| 64 | + |
| 65 | + function getKey(turn, [mr, mc], [cr, cc]) { |
| 66 | + return `${turn},${mr},${mc},${cr},${cc}`; |
| 67 | + } |
| 68 | + |
| 69 | + function isValid(r, c) { |
| 70 | + return r >= 0 && r < rows && c >= 0 && c < cols && grid[r][c] !== '#'; |
| 71 | + } |
| 72 | + |
| 73 | + function canWin(turn, mousePos, catPos) { |
| 74 | + if (turn >= available * 2) return false; |
| 75 | + |
| 76 | + const key = getKey(turn, mousePos, catPos); |
| 77 | + if (memo.has(key)) return memo.get(key); |
| 78 | + |
| 79 | + const isMouseTurn = turn % 2 === 0; |
| 80 | + const [r, c] = isMouseTurn ? mousePos : catPos; |
| 81 | + const maxJump = isMouseTurn ? mouseJump : catJump; |
| 82 | + |
| 83 | + for (const [dr, dc] of dirs) { |
| 84 | + for (let jump = 0; jump <= maxJump; jump++) { |
| 85 | + const nr = r + dr * jump; |
| 86 | + const nc = c + dc * jump; |
| 87 | + if (!isValid(nr, nc)) break; |
| 88 | + |
| 89 | + if (isMouseTurn) { |
| 90 | + if (grid[nr][nc] === 'F' || canWin(turn + 1, [nr, nc], catPos)) { |
| 91 | + memo.set(key, true); |
| 92 | + return true; |
| 93 | + } |
| 94 | + } else { |
| 95 | + if (grid[nr][nc] === 'F' || (nr === mousePos[0] && nc === mousePos[1]) |
| 96 | + || !canWin(turn + 1, mousePos, [nr, nc])) { |
| 97 | + memo.set(key, false); |
| 98 | + return false; |
| 99 | + } |
| 100 | + } |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + memo.set(key, !isMouseTurn); |
| 105 | + return !isMouseTurn; |
| 106 | + } |
| 107 | +}; |
0 commit comments