Welcome to Subscribe On Youtube

1728. Cat and Mouse II

Description

A game is played by a cat and a mouse named Cat and Mouse.

The environment is represented by a grid of size rows x cols, where each element is a wall, floor, player (Cat, Mouse), or food.

  • Players are represented by the characters 'C'(Cat),'M'(Mouse).
  • Floors are represented by the character '.' and can be walked on.
  • Walls are represented by the character '#' and cannot be walked on.
  • Food is represented by the character 'F' and can be walked on.
  • There is only one of each character 'C', 'M', and 'F' in grid.

Mouse and Cat play according to the following rules:

  • Mouse moves first, then they take turns to move.
  • During each turn, Cat and Mouse can jump in one of the four directions (left, right, up, down). They cannot jump over the wall nor outside of the grid.
  • catJump, mouseJump are the maximum lengths Cat and Mouse can jump at a time, respectively. Cat and Mouse can jump less than the maximum length.
  • Staying in the same position is allowed.
  • Mouse can jump over Cat.

The game can end in 4 ways:

  • If Cat occupies the same position as Mouse, Cat wins.
  • If Cat reaches the food first, Cat wins.
  • If Mouse reaches the food first, Mouse wins.
  • If Mouse cannot get to the food within 1000 turns, Cat wins.

Given a rows x cols matrix grid and two integers catJump and mouseJump, return true if Mouse can win the game if both Cat and Mouse play optimally, otherwise return false.

 

Example 1:

Input: grid = ["####F","#C...","M...."], catJump = 1, mouseJump = 2
Output: true
Explanation: Cat cannot catch Mouse on its turn nor can it get the food before Mouse.

Example 2:

Input: grid = ["M.C...F"], catJump = 1, mouseJump = 4
Output: true

Example 3:

Input: grid = ["M.C...F"], catJump = 1, mouseJump = 3
Output: false

 

Constraints:

  • rows == grid.length
  • cols = grid[i].length
  • 1 <= rows, cols <= 8
  • grid[i][j] consist only of characters 'C', 'M', 'F', '.', and '#'.
  • There is only one of each character 'C', 'M', and 'F' in grid.
  • 1 <= catJump, mouseJump <= 8

Solutions

  • class Solution:
        def canMouseWin(self, grid: List[str], catJump: int, mouseJump: int) -> bool:
            dirs = [0, 1, 0, -1, 0]
            m = len(grid)
            n = len(grid[0])
            nFloors = 0
            cat = 0  # cat's position
            mouse = 0  # mouse's position
    
            def hash(i: int, j: int) -> int:
                return i * n + j
    
            for i in range(m):
                for j in range(n):
                    if grid[i][j] != "#":
                        nFloors += 1
                    if grid[i][j] == "C":
                        cat = hash(i, j)
                    elif grid[i][j] == "M":
                        mouse = hash(i, j)
    
            # dp(i, j, k) := True if mouse can win w//
            # Cat on (i // 8, i % 8), mouse on (j // 8, j % 8), and turns = k
            @functools.lru_cache(None)
            def dp(cat: int, mouse: int, turn: int) -> bool:
                # We already search whole touchable grid
                if turn == nFloors * 2:
                    return False
    
                if turn % 2 == 0:
                    # mouse's turn
                    i = mouse // n
                    j = mouse % n
                    for k in range(4):
                        for jump in range(mouseJump + 1):
                            x = i + dirs[k] * jump
                            y = j + dirs[k + 1] * jump
                            if x < 0 or x == m or y < 0 or y == n:
                                break
                            if grid[x][y] == "#":
                                break
                            if grid[x][y] == "F":  # Mouse eats the food, so mouse win
                                return True
                            if dp(cat, hash(x, y), turn + 1):
                                return True
                    # Mouse can't win, so mouse lose
                    return False
                else:
                    # cat's turn
                    i = cat // n
                    j = cat % n
                    for k in range(4):
                        for jump in range(catJump + 1):
                            x = i + dirs[k] * jump
                            y = j + dirs[k + 1] * jump
                            if x < 0 or x == m or y < 0 or y == n:
                                break
                            if grid[x][y] == "#":
                                break
                            if grid[x][y] == "F":  # Cat eats the food, so mouse lose
                                return False
                            nextCat = hash(x, y)
                            if nextCat == mouse:  # Cat catches mouse, so mouse lose
                                return False
                            if not dp(nextCat, mouse, turn + 1):
                                return False
                    # Cat can't win, so mouse win
                    return True
    
            return dp(cat, mouse, 0)
    
    
  • class Solution {
        private final int[] dirs = {-1, 0, 1, 0, -1};
    
        public boolean canMouseWin(String[] grid, int catJump, int mouseJump) {
            int m = grid.length;
            int n = grid[0].length();
            int catStart = 0, mouseStart = 0, food = 0;
            List<Integer>[] gMouse = new List[m * n];
            List<Integer>[] gCat = new List[m * n];
            Arrays.setAll(gMouse, i -> new ArrayList<>());
            Arrays.setAll(gCat, i -> new ArrayList<>());
    
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    char c = grid[i].charAt(j);
                    if (c == '#') {
                        continue;
                    }
                    int v = i * n + j;
                    if (c == 'C') {
                        catStart = v;
                    } else if (c == 'M') {
                        mouseStart = v;
                    } else if (c == 'F') {
                        food = v;
                    }
    
                    for (int d = 0; d < 4; ++d) {
                        for (int k = 0; k <= mouseJump; k++) {
                            int x = i + k * dirs[d];
                            int y = j + k * dirs[d + 1];
                            if (x < 0 || x >= m || y < 0 || y >= n || grid[x].charAt(y) == '#') {
                                break;
                            }
                            gMouse[v].add(x * n + y);
                        }
                        for (int k = 0; k <= catJump; k++) {
                            int x = i + k * dirs[d];
                            int y = j + k * dirs[d + 1];
                            if (x < 0 || x >= m || y < 0 || y >= n || grid[x].charAt(y) == '#') {
                                break;
                            }
                            gCat[v].add(x * n + y);
                        }
                    }
                }
            }
    
            return calc(gMouse, gCat, mouseStart, catStart, food) == 1;
        }
    
        private int calc(
            List<Integer>[] gMouse, List<Integer>[] gCat, int mouseStart, int catStart, int hole) {
            int n = gMouse.length;
            int[][][] degree = new int[n][n][2];
            int[][][] ans = new int[n][n][2];
            Deque<int[]> q = new ArrayDeque<>();
    
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    degree[i][j][0] = gMouse[i].size();
                    degree[i][j][1] = gCat[j].size();
                }
            }
    
            for (int i = 0; i < n; i++) {
                ans[hole][i][1] = 1;
                ans[i][hole][0] = 2;
                ans[i][i][1] = 2;
                ans[i][i][0] = 2;
                q.offer(new int[] {hole, i, 1});
                q.offer(new int[] {i, hole, 0});
                q.offer(new int[] {i, i, 0});
                q.offer(new int[] {i, i, 1});
            }
    
            while (!q.isEmpty()) {
                int[] state = q.poll();
                int m = state[0], c = state[1], t = state[2];
                int result = ans[m][c][t];
                for (int[] prevState : getPrevStates(gMouse, gCat, state, ans)) {
                    int pm = prevState[0], pc = prevState[1], pt = prevState[2];
                    if (pt == result - 1) {
                        ans[pm][pc][pt] = result;
                        q.offer(prevState);
                    } else {
                        degree[pm][pc][pt]--;
                        if (degree[pm][pc][pt] == 0) {
                            ans[pm][pc][pt] = result;
                            q.offer(prevState);
                        }
                    }
                }
            }
    
            return ans[mouseStart][catStart][0];
        }
    
        private List<int[]> getPrevStates(
            List<Integer>[] gMouse, List<Integer>[] gCat, int[] state, int[][][] ans) {
            int m = state[0], c = state[1], t = state[2];
            int pt = t ^ 1;
            List<int[]> pre = new ArrayList<>();
            if (pt == 1) {
                for (int pc : gCat[c]) {
                    if (ans[m][pc][1] == 0) {
                        pre.add(new int[] {m, pc, pt});
                    }
                }
            } else {
                for (int pm : gMouse[m]) {
                    if (ans[pm][c][0] == 0) {
                        pre.add(new int[] {pm, c, 0});
                    }
                }
            }
            return pre;
        }
    }
    
    
  • class Solution {
    private:
        const int dirs[5] = {-1, 0, 1, 0, -1};
    
        int calc(vector<vector<int>>& gMouse, vector<vector<int>>& gCat, int mouseStart, int catStart, int hole) {
            int n = gMouse.size();
            vector<vector<vector<int>>> degree(n, vector<vector<int>>(n, vector<int>(2)));
            vector<vector<vector<int>>> ans(n, vector<vector<int>>(n, vector<int>(2)));
            queue<tuple<int, int, int>> q;
    
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < n; j++) {
                    degree[i][j][0] = gMouse[i].size();
                    degree[i][j][1] = gCat[j].size();
                }
            }
    
            for (int i = 0; i < n; i++) {
                ans[hole][i][1] = 1;
                ans[i][hole][0] = 2;
                ans[i][i][1] = 2;
                ans[i][i][0] = 2;
                q.push(make_tuple(hole, i, 1));
                q.push(make_tuple(i, hole, 0));
                q.push(make_tuple(i, i, 0));
                q.push(make_tuple(i, i, 1));
            }
    
            while (!q.empty()) {
                auto state = q.front();
                q.pop();
                int m = get<0>(state), c = get<1>(state), t = get<2>(state);
                int result = ans[m][c][t];
                for (auto& prevState : getPrevStates(gMouse, gCat, state, ans)) {
                    int pm = get<0>(prevState), pc = get<1>(prevState), pt = get<2>(prevState);
                    if (pt == result - 1) {
                        ans[pm][pc][pt] = result;
                        q.push(prevState);
                    } else {
                        degree[pm][pc][pt]--;
                        if (degree[pm][pc][pt] == 0) {
                            ans[pm][pc][pt] = result;
                            q.push(prevState);
                        }
                    }
                }
            }
    
            return ans[mouseStart][catStart][0];
        }
    
        vector<tuple<int, int, int>> getPrevStates(vector<vector<int>>& gMouse, vector<vector<int>>& gCat, tuple<int, int, int>& state, vector<vector<vector<int>>>& ans) {
            int m = get<0>(state), c = get<1>(state), t = get<2>(state);
            int pt = t ^ 1;
            vector<tuple<int, int, int>> pre;
            if (pt == 1) {
                for (int pc : gCat[c]) {
                    if (ans[m][pc][1] == 0) {
                        pre.push_back(make_tuple(m, pc, pt));
                    }
                }
            } else {
                for (int pm : gMouse[m]) {
                    if (ans[pm][c][0] == 0) {
                        pre.push_back(make_tuple(pm, c, 0));
                    }
                }
            }
            return pre;
        }
    
    public:
        bool canMouseWin(vector<string>& grid, int catJump, int mouseJump) {
            int m = grid.size();
            int n = grid[0].length();
            int catStart = 0, mouseStart = 0, food = 0;
            vector<vector<int>> gMouse(m * n);
            vector<vector<int>> gCat(m * n);
    
            for (int i = 0; i < m; i++) {
                for (int j = 0; j < n; j++) {
                    char c = grid[i][j];
                    if (c == '#') {
                        continue;
                    }
                    int v = i * n + j;
                    if (c == 'C') {
                        catStart = v;
                    } else if (c == 'M') {
                        mouseStart = v;
                    } else if (c == 'F') {
                        food = v;
                    }
    
                    for (int d = 0; d < 4; ++d) {
                        for (int k = 0; k <= mouseJump; k++) {
                            int x = i + k * dirs[d];
                            int y = j + k * dirs[d + 1];
                            if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == '#') {
                                break;
                            }
                            gMouse[v].push_back(x * n + y);
                        }
                        for (int k = 0; k <= catJump; k++) {
                            int x = i + k * dirs[d];
                            int y = j + k * dirs[d + 1];
                            if (x < 0 || x >= m || y < 0 || y >= n || grid[x][y] == '#') {
                                break;
                            }
                            gCat[v].push_back(x * n + y);
                        }
                    }
                }
            }
    
            return calc(gMouse, gCat, mouseStart, catStart, food) == 1;
        }
    };
    
    
  • func canMouseWin(grid []string, catJump int, mouseJump int) bool {
    	m, n := len(grid), len(grid[0])
    	catStart, mouseStart, food := 0, 0, 0
    	dirs := []int{-1, 0, 1, 0, -1}
    	gMouse := make([][]int, m*n)
    	gCat := make([][]int, m*n)
    
    	for i := 0; i < m; i++ {
    		for j := 0; j < n; j++ {
    			c := grid[i][j]
    			if c == '#' {
    				continue
    			}
    			v := i*n + j
    			if c == 'C' {
    				catStart = v
    			} else if c == 'M' {
    				mouseStart = v
    			} else if c == 'F' {
    				food = v
    			}
    			for d := 0; d < 4; d++ {
    				a, b := dirs[d], dirs[d+1]
    				for k := 0; k <= mouseJump; k++ {
    					x, y := i+k*a, j+k*b
    					if !(0 <= x && x < m && 0 <= y && y < n && grid[x][y] != '#') {
    						break
    					}
    					gMouse[v] = append(gMouse[v], x*n+y)
    				}
    				for k := 0; k <= catJump; k++ {
    					x, y := i+k*a, j+k*b
    					if !(0 <= x && x < m && 0 <= y && y < n && grid[x][y] != '#') {
    						break
    					}
    					gCat[v] = append(gCat[v], x*n+y)
    				}
    			}
    		}
    	}
    	return calc(gMouse, gCat, mouseStart, catStart, food) == 1
    }
    
    func calc(gMouse, gCat [][]int, mouseStart, catStart, hole int) int {
    	n := len(gMouse)
    	degree := make([][][]int, n)
    	ans := make([][][]int, n)
    	for i := 0; i < n; i++ {
    		degree[i] = make([][]int, n)
    		ans[i] = make([][]int, n)
    		for j := 0; j < n; j++ {
    			degree[i][j] = make([]int, 2)
    			ans[i][j] = make([]int, 2)
    			degree[i][j][0] = len(gMouse[i])
    			degree[i][j][1] = len(gCat[j])
    		}
    	}
    
    	q := list.New()
    	for i := 0; i < n; i++ {
    		ans[hole][i][1] = 1
    		ans[i][hole][0] = 2
    		ans[i][i][1] = 2
    		ans[i][i][0] = 2
    		q.PushBack([]int{hole, i, 1})
    		q.PushBack([]int{i, hole, 0})
    		q.PushBack([]int{i, i, 0})
    		q.PushBack([]int{i, i, 1})
    	}
    
    	for q.Len() > 0 {
    		front := q.Front()
    		q.Remove(front)
    		state := front.Value.([]int)
    		m, c, t := state[0], state[1], state[2]
    		currentAns := ans[m][c][t]
    		for _, prevState := range getPrevStates(gMouse, gCat, m, c, t, ans) {
    			pm, pc, pt := prevState[0], prevState[1], prevState[2]
    			if pt == currentAns-1 {
    				ans[pm][pc][pt] = currentAns
    				q.PushBack([]int{pm, pc, pt})
    			} else {
    				degree[pm][pc][pt]--
    				if degree[pm][pc][pt] == 0 {
    					ans[pm][pc][pt] = currentAns
    					q.PushBack([]int{pm, pc, pt})
    				}
    			}
    		}
    	}
    	return ans[mouseStart][catStart][0]
    }
    
    func getPrevStates(gMouse, gCat [][]int, m, c, t int, ans [][][]int) [][]int {
    	pt := t ^ 1
    	pre := [][]int{}
    	if pt == 1 {
    		for _, pc := range gCat[c] {
    			if ans[m][pc][1] == 0 {
    				pre = append(pre, []int{m, pc, pt})
    			}
    		}
    	} else {
    		for _, pm := range gMouse[m] {
    			if ans[pm][c][0] == 0 {
    				pre = append(pre, []int{pm, c, pt})
    			}
    		}
    	}
    	return pre
    }
    
    
  • function canMouseWin(grid: string[], catJump: number, mouseJump: number): boolean {
        const m = grid.length;
        const n = grid[0].length;
    
        let catStart = 0;
        let mouseStart = 0;
        let food = 0;
    
        const dirs = [-1, 0, 1, 0, -1];
    
        const gMouse: number[][] = Array.from({ length: m * n }, () => []);
        const gCat: number[][] = Array.from({ length: m * n }, () => []);
    
        for (let i = 0; i < m; i++) {
            for (let j = 0; j < n; j++) {
                const c = grid[i][j];
    
                if (c === '#') continue;
    
                const v = i * n + j;
    
                if (c === 'C') catStart = v;
                else if (c === 'M') mouseStart = v;
                else if (c === 'F') food = v;
    
                for (let d = 0; d < 4; d++) {
                    const a = dirs[d];
                    const b = dirs[d + 1];
    
                    for (let k = 0; k <= mouseJump; k++) {
                        const x = i + k * a;
                        const y = j + k * b;
    
                        if (!(0 <= x && x < m && 0 <= y && y < n && grid[x][y] !== '#')) break;
    
                        gMouse[v].push(x * n + y);
                    }
    
                    for (let k = 0; k <= catJump; k++) {
                        const x = i + k * a;
                        const y = j + k * b;
    
                        if (!(0 <= x && x < m && 0 <= y && y < n && grid[x][y] !== '#')) break;
    
                        gCat[v].push(x * n + y);
                    }
                }
            }
        }
    
        function getPrevStates(m: number, c: number, t: number, ans: number[][][]): number[][] {
            const pt = t ^ 1;
            const pre: number[][] = [];
    
            if (pt === 1) {
                for (const pc of gCat[c]) {
                    if (ans[m][pc][1] === 0) pre.push([m, pc, pt]);
                }
            } else {
                for (const pm of gMouse[m]) {
                    if (ans[pm][c][0] === 0) pre.push([pm, c, pt]);
                }
            }
    
            return pre;
        }
    
        function calc(): number {
            const N = m * n;
    
            const degree: number[][][] = Array.from({ length: N }, () =>
                Array.from({ length: N }, () => [0, 0]),
            );
    
            const ans: number[][][] = Array.from({ length: N }, () =>
                Array.from({ length: N }, () => [0, 0]),
            );
    
            for (let i = 0; i < N; i++) {
                for (let j = 0; j < N; j++) {
                    degree[i][j][0] = gMouse[i].length;
                    degree[i][j][1] = gCat[j].length;
                }
            }
    
            const q: number[][] = [];
    
            for (let i = 0; i < N; i++) {
                ans[food][i][1] = 1;
                ans[i][food][0] = 2;
                ans[i][i][1] = 2;
                ans[i][i][0] = 2;
    
                q.push([food, i, 1]);
                q.push([i, food, 0]);
                q.push([i, i, 0]);
                q.push([i, i, 1]);
            }
    
            while (q.length) {
                const [mPos, cPos, t] = q.shift()!;
                const currentAns = ans[mPos][cPos][t];
    
                for (const [pm, pc, pt] of getPrevStates(mPos, cPos, t, ans)) {
                    if (pt === currentAns - 1) {
                        ans[pm][pc][pt] = currentAns;
                        q.push([pm, pc, pt]);
                    } else {
                        degree[pm][pc][pt]--;
                        if (degree[pm][pc][pt] === 0) {
                            ans[pm][pc][pt] = currentAns;
                            q.push([pm, pc, pt]);
                        }
                    }
                }
            }
    
            return ans[mouseStart][catStart][0];
        }
    
        return calc() === 1;
    }
    
    
  • impl Solution {
        pub fn can_mouse_win(grid: Vec<String>, cat_jump: i32, mouse_jump: i32) -> bool {
            let m = grid.len();
            let n = grid[0].len();
    
            let grid: Vec<Vec<char>> = grid.iter().map(|s| s.chars().collect()).collect();
    
            let mut cat_start = 0usize;
            let mut mouse_start = 0usize;
            let mut food = 0usize;
    
            let dirs = [-1, 0, 1, 0, -1];
    
            let mut g_mouse = vec![Vec::<usize>::new(); m * n];
            let mut g_cat = vec![Vec::<usize>::new(); m * n];
    
            for i in 0..m {
                for j in 0..n {
                    let c = grid[i][j];
                    if c == '#' {
                        continue;
                    }
    
                    let v = i * n + j;
    
                    if c == 'C' {
                        cat_start = v;
                    } else if c == 'M' {
                        mouse_start = v;
                    } else if c == 'F' {
                        food = v;
                    }
    
                    for d in 0..4 {
                        let a = dirs[d];
                        let b = dirs[d + 1];
    
                        for k in 0..=mouse_jump {
                            let x = i as i32 + k * a;
                            let y = j as i32 + k * b;
    
                            if !(x >= 0
                                && x < m as i32
                                && y >= 0
                                && y < n as i32
                                && grid[x as usize][y as usize] != '#')
                            {
                                break;
                            }
    
                            g_mouse[v].push((x as usize) * n + y as usize);
                        }
    
                        for k in 0..=cat_jump {
                            let x = i as i32 + k * a;
                            let y = j as i32 + k * b;
    
                            if !(x >= 0
                                && x < m as i32
                                && y >= 0
                                && y < n as i32
                                && grid[x as usize][y as usize] != '#')
                            {
                                break;
                            }
    
                            g_cat[v].push((x as usize) * n + y as usize);
                        }
                    }
                }
            }
    
            use std::collections::VecDeque;
    
            let n2 = m * n;
    
            let mut degree = vec![vec![vec![0i32; 2]; n2]; n2];
            let mut ans = vec![vec![vec![0i32; 2]; n2]; n2];
    
            for i in 0..n2 {
                for j in 0..n2 {
                    degree[i][j][0] = g_mouse[i].len() as i32;
                    degree[i][j][1] = g_cat[j].len() as i32;
                }
            }
    
            let mut q = VecDeque::new();
    
            for i in 0..n2 {
                ans[food][i][1] = 1;
                ans[i][food][0] = 2;
                ans[i][i][1] = 2;
                ans[i][i][0] = 2;
    
                q.push_back((food, i, 1));
                q.push_back((i, food, 0));
                q.push_back((i, i, 0));
                q.push_back((i, i, 1));
            }
    
            while let Some((m_pos, c_pos, t)) = q.pop_front() {
                let current_ans = ans[m_pos][c_pos][t];
    
                let pt = t ^ 1;
    
                if pt == 1 {
                    for &pc in &g_cat[c_pos] {
                        if ans[m_pos][pc][1] != 0 {
                            continue;
                        }
    
                        if pt as i32 == current_ans - 1 {
                            ans[m_pos][pc][1] = current_ans;
                            q.push_back((m_pos, pc, 1));
                        } else {
                            degree[m_pos][pc][1] -= 1;
                            if degree[m_pos][pc][1] == 0 {
                                ans[m_pos][pc][1] = current_ans;
                                q.push_back((m_pos, pc, 1));
                            }
                        }
                    }
                } else {
                    for &pm in &g_mouse[m_pos] {
                        if ans[pm][c_pos][0] != 0 {
                            continue;
                        }
    
                        if pt as i32 == current_ans - 1 {
                            ans[pm][c_pos][0] = current_ans;
                            q.push_back((pm, c_pos, 0));
                        } else {
                            degree[pm][c_pos][0] -= 1;
                            if degree[pm][c_pos][0] == 0 {
                                ans[pm][c_pos][0] = current_ans;
                                q.push_back((pm, c_pos, 0));
                            }
                        }
                    }
                }
            }
    
            ans[mouse_start][cat_start][0] == 1
        }
    }
    
    

All Problems

All Solutions