diff --git a/solution/1700-1799/1728.Cat and Mouse II/README.md b/solution/1700-1799/1728.Cat and Mouse II/README.md index a46c8445c1561..7c65074a30831 100644 --- a/solution/1700-1799/1728.Cat and Mouse II/README.md +++ b/solution/1700-1799/1728.Cat and Mouse II/README.md @@ -119,7 +119,31 @@ tags: -### 方法一 +### 方法一:拓扑排序 + +根据题目描述,游戏中的状态由老鼠的位置、猫的位置和移动方决定。当状态为以下情况,可以直接确定胜负: + +- 当猫和老鼠的位置相同时,猫获胜,这是猫的必胜状态,老鼠的必败状态。 +- 当猫先到达食物时,猫获胜,这是猫的必胜状态,老鼠的必败状态。 +- 当老鼠先到达食物时,老鼠获胜,这是老鼠的必胜状态,猫的必败状态。 + +为了得到初始状态的游戏结果,需要从边界状态开始遍历所有的状态。每个状态包含老鼠的位置、猫的位置和移动方,根据当前状态可以得到上一轮的所有可能状态,上一轮状态的移动方和当前状态的移动方相反,上一轮状态的移动方在上一轮状态的位置和当前状态的位置不同。 + +我们用元组 $(m, c, t)$ 表示本轮的状态,用 $(pm, pc, pt)$ 表示上一轮可能的状态,那么上一轮的所有可能状态有: + +- 如果本轮的移动方是老鼠,那么上一轮的移动方是猫,上一轮的老鼠位置是本轮老鼠位置,上一轮的猫位置是本轮猫位置的所有邻接点。 +- 如果本轮的移动方是猫,那么上一轮的移动方是老鼠,上一轮的猫位置是本轮猫位置,上一轮的老鼠位置是本轮老鼠位置的所有邻接点。 + +初始时,除了边界状态以外,其他所有状态的结果都是未知的。我们从边界状态开始,对于每个状态,得到上一轮的所有可能状态并更新结果,更新的逻辑如下: + +1. 如果上一轮的移动方与本轮的获胜方相同,那么上一轮的移动方可以到达当前状态并获胜,直接更新上一轮的状态为本轮的获胜方。 +1. 如果上一轮的移动方与本轮的获胜方不同,且上一轮的移动方可以到达的所有状态都是上一轮的移动方的必败状态,那么我们将上一轮的状态更新为本轮的获胜方。 + +对于第 $2$ 个更新逻辑,我们需要记录每个状态的度。初始时,每个状态的度表示该状态的移动方可以移动到的结点数,即移动方所在节点的相邻结点数,如果移动方是猫且所在结点与洞相邻则需要将该状态的度减 $1$。 + +当所有状态的结果都更新完毕时,初始状态的结果即为最终结果。 + +时间复杂度 $O(m^2 \times n^2 \times (m + n)$,空间复杂度 $O(m^2 \times n^2)$。其中 $m$ 和 $n$ 分别是矩阵的行数和列数。 @@ -128,74 +152,452 @@ tags: ```python 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: + m, n = len(grid), len(grid[0]) + cat_start = mouse_start = food = 0 + dirs = (-1, 0, 1, 0, -1) + g_mouse = [[] for _ in range(m * n)] + g_cat = [[] for _ in range(m * n)] + + for i, row in enumerate(grid): + for j, c in enumerate(row): + if c == "#": + continue + v = i * n + j + if c == "C": + cat_start = v + elif c == "M": + mouse_start = v + elif c == "F": + food = v + for a, b in pairwise(dirs): + for k in range(mouseJump + 1): + x, y = i + k * a, j + k * b + if not (0 <= x < m and 0 <= y < n and grid[x][y] != "#"): break - if grid[x][y] == "#": + g_mouse[v].append(x * n + y) + for k in range(catJump + 1): + x, y = i + k * a, j + k * b + if not (0 <= x < m and 0 <= y < n and 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 + g_cat[v].append(x * n + y) + return self.calc(g_mouse, g_cat, mouse_start, cat_start, food) == 1 + + def calc( + self, + g_mouse: List[List[int]], + g_cat: List[List[int]], + mouse_start: int, + cat_start: int, + hole: int, + ) -> int: + def get_prev_states(state): + m, c, t = state + pt = t ^ 1 + pre = [] + if pt == 1: + for pc in g_cat[c]: + if ans[m][pc][1] == 0: + pre.append((m, pc, pt)) 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) + for pm in g_mouse[m]: + if ans[pm][c][0] == 0: + pre.append((pm, c, 0)) + return pre + + n = len(g_mouse) + degree = [[[0, 0] for _ in range(n)] for _ in range(n)] + for i in range(n): + for j in range(n): + degree[i][j][0] = len(g_mouse[i]) + degree[i][j][1] = len(g_cat[j]) + + ans = [[[0, 0] for _ in range(n)] for _ in range(n)] + q = deque() + for i in range(n): + ans[hole][i][1] = 1 + ans[i][hole][0] = 2 + ans[i][i][1] = ans[i][i][0] = 2 + q.append((hole, i, 1)) + q.append((i, hole, 0)) + q.append((i, i, 0)) + q.append((i, i, 1)) + while q: + state = q.popleft() + t = ans[state[0]][state[1]][state[2]] + for prev_state in get_prev_states(state): + pm, pc, pt = prev_state + if pt == t - 1: + ans[pm][pc][pt] = t + q.append(prev_state) + else: + degree[pm][pc][pt] -= 1 + if degree[pm][pc][pt] == 0: + ans[pm][pc][pt] = t + q.append(prev_state) + return ans[mouse_start][cat_start][0] +``` + +#### Java + +```java +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[] gMouse = new List[m * n]; + List[] 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[] gMouse, List[] 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 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 getPrevStates( + List[] gMouse, List[] gCat, int[] state, int[][][] ans) { + int m = state[0], c = state[1], t = state[2]; + int pt = t ^ 1; + List 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; + } +} +``` + +#### C++ + +```cpp +class Solution { +private: + const int dirs[5] = {-1, 0, 1, 0, -1}; + + int calc(vector>& gMouse, vector>& gCat, int mouseStart, int catStart, int hole) { + int n = gMouse.size(); + vector>> degree(n, vector>(n, vector(2))); + vector>> ans(n, vector>(n, vector(2))); + queue> 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> getPrevStates(vector>& gMouse, vector>& gCat, tuple& state, vector>>& ans) { + int m = get<0>(state), c = get<1>(state), t = get<2>(state); + int pt = t ^ 1; + vector> 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& grid, int catJump, int mouseJump) { + int m = grid.size(); + int n = grid[0].length(); + int catStart = 0, mouseStart = 0, food = 0; + vector> gMouse(m * n); + vector> 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; + } +}; +``` + +#### Go + +```go +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 +} ``` diff --git a/solution/1700-1799/1728.Cat and Mouse II/README_EN.md b/solution/1700-1799/1728.Cat and Mouse II/README_EN.md index fc9bdea13cf05..c147023741405 100644 --- a/solution/1700-1799/1728.Cat and Mouse II/README_EN.md +++ b/solution/1700-1799/1728.Cat and Mouse II/README_EN.md @@ -108,74 +108,452 @@ tags: ```python 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: + m, n = len(grid), len(grid[0]) + cat_start = mouse_start = food = 0 + dirs = (-1, 0, 1, 0, -1) + g_mouse = [[] for _ in range(m * n)] + g_cat = [[] for _ in range(m * n)] + + for i, row in enumerate(grid): + for j, c in enumerate(row): + if c == "#": + continue + v = i * n + j + if c == "C": + cat_start = v + elif c == "M": + mouse_start = v + elif c == "F": + food = v + for a, b in pairwise(dirs): + for k in range(mouseJump + 1): + x, y = i + k * a, j + k * b + if not (0 <= x < m and 0 <= y < n and grid[x][y] != "#"): break - if grid[x][y] == "#": + g_mouse[v].append(x * n + y) + for k in range(catJump + 1): + x, y = i + k * a, j + k * b + if not (0 <= x < m and 0 <= y < n and 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 + g_cat[v].append(x * n + y) + return self.calc(g_mouse, g_cat, mouse_start, cat_start, food) == 1 + + def calc( + self, + g_mouse: List[List[int]], + g_cat: List[List[int]], + mouse_start: int, + cat_start: int, + hole: int, + ) -> int: + def get_prev_states(state): + m, c, t = state + pt = t ^ 1 + pre = [] + if pt == 1: + for pc in g_cat[c]: + if ans[m][pc][1] == 0: + pre.append((m, pc, pt)) 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) + for pm in g_mouse[m]: + if ans[pm][c][0] == 0: + pre.append((pm, c, 0)) + return pre + + n = len(g_mouse) + degree = [[[0, 0] for _ in range(n)] for _ in range(n)] + for i in range(n): + for j in range(n): + degree[i][j][0] = len(g_mouse[i]) + degree[i][j][1] = len(g_cat[j]) + + ans = [[[0, 0] for _ in range(n)] for _ in range(n)] + q = deque() + for i in range(n): + ans[hole][i][1] = 1 + ans[i][hole][0] = 2 + ans[i][i][1] = ans[i][i][0] = 2 + q.append((hole, i, 1)) + q.append((i, hole, 0)) + q.append((i, i, 0)) + q.append((i, i, 1)) + while q: + state = q.popleft() + t = ans[state[0]][state[1]][state[2]] + for prev_state in get_prev_states(state): + pm, pc, pt = prev_state + if pt == t - 1: + ans[pm][pc][pt] = t + q.append(prev_state) + else: + degree[pm][pc][pt] -= 1 + if degree[pm][pc][pt] == 0: + ans[pm][pc][pt] = t + q.append(prev_state) + return ans[mouse_start][cat_start][0] +``` + +#### Java + +```java +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[] gMouse = new List[m * n]; + List[] 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[] gMouse, List[] 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 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 getPrevStates( + List[] gMouse, List[] gCat, int[] state, int[][][] ans) { + int m = state[0], c = state[1], t = state[2]; + int pt = t ^ 1; + List 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; + } +} +``` + +#### C++ + +```cpp +class Solution { +private: + const int dirs[5] = {-1, 0, 1, 0, -1}; + + int calc(vector>& gMouse, vector>& gCat, int mouseStart, int catStart, int hole) { + int n = gMouse.size(); + vector>> degree(n, vector>(n, vector(2))); + vector>> ans(n, vector>(n, vector(2))); + queue> 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> getPrevStates(vector>& gMouse, vector>& gCat, tuple& state, vector>>& ans) { + int m = get<0>(state), c = get<1>(state), t = get<2>(state); + int pt = t ^ 1; + vector> 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& grid, int catJump, int mouseJump) { + int m = grid.size(); + int n = grid[0].length(); + int catStart = 0, mouseStart = 0, food = 0; + vector> gMouse(m * n); + vector> 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; + } +}; +``` + +#### Go + +```go +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 +} ``` diff --git a/solution/1700-1799/1728.Cat and Mouse II/Solution.cpp b/solution/1700-1799/1728.Cat and Mouse II/Solution.cpp new file mode 100644 index 0000000000000..002085c1ec15b --- /dev/null +++ b/solution/1700-1799/1728.Cat and Mouse II/Solution.cpp @@ -0,0 +1,118 @@ +class Solution { +private: + const int dirs[5] = {-1, 0, 1, 0, -1}; + + int calc(vector>& gMouse, vector>& gCat, int mouseStart, int catStart, int hole) { + int n = gMouse.size(); + vector>> degree(n, vector>(n, vector(2))); + vector>> ans(n, vector>(n, vector(2))); + queue> 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> getPrevStates(vector>& gMouse, vector>& gCat, tuple& state, vector>>& ans) { + int m = get<0>(state), c = get<1>(state), t = get<2>(state); + int pt = t ^ 1; + vector> 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& grid, int catJump, int mouseJump) { + int m = grid.size(); + int n = grid[0].length(); + int catStart = 0, mouseStart = 0, food = 0; + vector> gMouse(m * n); + vector> 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; + } +}; diff --git a/solution/1700-1799/1728.Cat and Mouse II/Solution.go b/solution/1700-1799/1728.Cat and Mouse II/Solution.go new file mode 100644 index 0000000000000..0c4c7bd1f6756 --- /dev/null +++ b/solution/1700-1799/1728.Cat and Mouse II/Solution.go @@ -0,0 +1,111 @@ +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 +} diff --git a/solution/1700-1799/1728.Cat and Mouse II/Solution.java b/solution/1700-1799/1728.Cat and Mouse II/Solution.java new file mode 100644 index 0000000000000..91b5601933fed --- /dev/null +++ b/solution/1700-1799/1728.Cat and Mouse II/Solution.java @@ -0,0 +1,119 @@ +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[] gMouse = new List[m * n]; + List[] 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[] gMouse, List[] 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 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 getPrevStates( + List[] gMouse, List[] gCat, int[] state, int[][][] ans) { + int m = state[0], c = state[1], t = state[2]; + int pt = t ^ 1; + List 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; + } +} diff --git a/solution/1700-1799/1728.Cat and Mouse II/Solution.py b/solution/1700-1799/1728.Cat and Mouse II/Solution.py index 7154a272c06d1..c7168a32bd3a7 100644 --- a/solution/1700-1799/1728.Cat and Mouse II/Solution.py +++ b/solution/1700-1799/1728.Cat and Mouse II/Solution.py @@ -1,70 +1,85 @@ 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 + m, n = len(grid), len(grid[0]) + cat_start = mouse_start = food = 0 + dirs = (-1, 0, 1, 0, -1) + g_mouse = [[] for _ in range(m * n)] + g_cat = [[] for _ in range(m * n)] - 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: + for i, row in enumerate(grid): + for j, c in enumerate(row): + if c == "#": + continue + v = i * n + j + if c == "C": + cat_start = v + elif c == "M": + mouse_start = v + elif c == "F": + food = v + for a, b in pairwise(dirs): + for k in range(mouseJump + 1): + x, y = i + k * a, j + k * b + if not (0 <= x < m and 0 <= y < n and grid[x][y] != "#"): break - if grid[x][y] == "#": + g_mouse[v].append(x * n + y) + for k in range(catJump + 1): + x, y = i + k * a, j + k * b + if not (0 <= x < m and 0 <= y < n and 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 + g_cat[v].append(x * n + y) + return self.calc(g_mouse, g_cat, mouse_start, cat_start, food) == 1 + + def calc( + self, + g_mouse: List[List[int]], + g_cat: List[List[int]], + mouse_start: int, + cat_start: int, + hole: int, + ) -> int: + def get_prev_states(state): + m, c, t = state + pt = t ^ 1 + pre = [] + if pt == 1: + for pc in g_cat[c]: + if ans[m][pc][1] == 0: + pre.append((m, pc, pt)) 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 + for pm in g_mouse[m]: + if ans[pm][c][0] == 0: + pre.append((pm, c, 0)) + return pre + + n = len(g_mouse) + degree = [[[0, 0] for _ in range(n)] for _ in range(n)] + for i in range(n): + for j in range(n): + degree[i][j][0] = len(g_mouse[i]) + degree[i][j][1] = len(g_cat[j]) - return dp(cat, mouse, 0) + ans = [[[0, 0] for _ in range(n)] for _ in range(n)] + q = deque() + for i in range(n): + ans[hole][i][1] = 1 + ans[i][hole][0] = 2 + ans[i][i][1] = ans[i][i][0] = 2 + q.append((hole, i, 1)) + q.append((i, hole, 0)) + q.append((i, i, 0)) + q.append((i, i, 1)) + while q: + state = q.popleft() + t = ans[state[0]][state[1]][state[2]] + for prev_state in get_prev_states(state): + pm, pc, pt = prev_state + if pt == t - 1: + ans[pm][pc][pt] = t + q.append(prev_state) + else: + degree[pm][pc][pt] -= 1 + if degree[pm][pc][pt] == 0: + ans[pm][pc][pt] = t + q.append(prev_state) + return ans[mouse_start][cat_start][0]