913. 猫和老鼠

exiaohu 于 2022-01-04 发布

题目链接:913. 猫和老鼠

动态规划。

几个概念:

  1. 必胜状态,如果猫的下一步就能赢,那么这个状态就是猫的必胜状态,同时,如果轮到猫走,且猫可以走一步使状态变为必胜状态,那这个状态也是猫的必胜状态;
  2. 必败状态,如果这个状态是猫的必胜状态,那它即是老鼠的必败状态;
  3. 必和状态,如果轮到猫走,但无法将状态变为猫的必胜状态,此时如果可以使状态也无法变为老鼠的必胜状态,那么猫可以将其变为必和状态,反之亦然。

记忆化搜索,从初始状态开始,memory[(mouse, cat, turns))] 表示在第 turns 步时,老鼠在 mouse 结点上,猫在 cat 结点上,这个点的状态(取值 0, 1, 2)。递归搜索,直到搜索到终止条件:

Note: 如果游戏已经进行了 2n 轮,n 为总的结点数目,还没有结束,那么这个状态就是必和状态。因为此时,除了 0 结点,老鼠已经到达过了所有的剩余结点,猫也到达过了所有的非 0 结点。此时,由于猫和老鼠都是按照最优策略在进行游戏,猫和老鼠都无法获胜。

from typing import List


class Solution:
    DRAW = 0
    MOUSE_WIN = 1
    CAT_WIN = 2

    def catMouseGame(self, graph: List[List[int]]) -> int:
        n, memory = len(graph), dict()

        def get_result(mouse: int, cat: int, turns: int) -> int:
            if turns == n * 2:
                return self.DRAW
            if (mouse, cat, turns) in memory:
                return memory.get((mouse, cat, turns))

            if mouse == 0:
                res = self.MOUSE_WIN
            elif cat == mouse:
                res = self.CAT_WIN
            else:
                res = get_next_result(mouse, cat, turns)

            memory[(mouse, cat, turns)] = res
            return res

        def get_next_result(mouse: int, cat: int, turns: int) -> int:
            cur_move = mouse if turns % 2 == 0 else cat
            default_res = self.MOUSE_WIN if cur_move != mouse else self.CAT_WIN
            res = default_res
            for next_node in graph[cur_move]:
                if cur_move == cat and next_node == 0:
                    continue
                next_mouse = next_node if cur_move == mouse else mouse
                next_cat = next_node if cur_move == cat else cat
                next_res = get_result(next_mouse, next_cat, turns + 1)
                if next_res != default_res:
                    res = next_res
                    if res != self.DRAW:
                        break
            return res

        return get_result(1, 2, 0)