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
    }
    
    

All Problems

All Solutions