力扣 913.猫和老鼠

题目来源:https://leetcode-cn.com/problems/cat-and-mouse/

大致题意:
给一个无向图,老鼠从节点 1 出发,猫猫从节点 2 出发。游戏规则为:

  • 老鼠先移动,然后猫猫移动
  • 如果老鼠移动到了节点 0,那么老鼠获胜
  • 如果猫猫移动到了老鼠的位置(猫猫不能到达节点 0),那么猫猫获胜
  • 如果老鼠或猫猫移动到重复位置,那么平局

返回游戏结果

思路

动态规划

使用 dp[mouse][cat][turn] 表示第 turn 轮移动后老鼠在 mouse 位置,猫猫在 cat 位置
那么有:

  • mouse = 0,老鼠到节点 0,老鼠获胜
  • cat = mouse,猫猫到达老鼠的位置,猫猫获胜
  • turn = 2*n,n 为节点数量,所有的节点已经到达过,双方都无法获胜,怕平局

对于老鼠而言:

  • 下一次的移动可以使老鼠接下来到节点 0,那么当前位置和接下来的移动位置就是必胜状态
  • 下一次的移动不可以到节点 0,那么尽量寻找平局的移动点,也就是平局状态;否则为必败状态

对于猫猫而言:

  • 下一次的移动如果可以使猫猫接下来到达老鼠的位置,那么当前位置和接下来的移动位置就是必胜状态
  • 下一次移动不能使猫猫追上老鼠,那么尽量寻找平局的移动点,也就是平局状态;否则为必败状态

可见老鼠和猫猫的移动有共同点,可以抽象为

  • 初始时默认下一次移动的状态为必败状态
  • 遍历接下来移动可能有的结果,如果存在必胜状态,那么直接返回结果(跳出循环)
  • 如果有平局状态,那么更新下一次移动状态为平局状态,并继续遍历,寻找是否有必胜状态(继续循环)
  • 如果没有碰到必胜状态和平局状态,那么即返回初始的必败状态

代码:

public class CatMouseGame {
    static final int MOUSE_WIN = 1;
    static final int CAT_WIN = 2;
    static final int DRAW = 0;
    int[][] graph;
    int[][][] dp;
    int n;
    public int catMouseGame(int[][] graph) {
        this.graph = graph;
        this.n = graph.length;
        this.dp = new int[n][n][2 * n];
        // 初始化状态数组
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                Arrays.fill(dp[i][j], -1);
            }
        }
        return getResult(1, 2, 0);
    }

    public int getResult(int mouse, int cat, int turn) {
        // 所有节点已经移动过,平局
        if (turn == 2 * n) {
            return DRAW;
        }
        // 若对应位置还未移动过
        if (dp[mouse][cat][turn] < 0) {
            if (mouse == 0) {   // 老鼠到达节点 0,老鼠胜
                dp[mouse][cat][turn] = MOUSE_WIN;
            } else if (mouse == cat) {  // 猫猫追上老鼠,猫猫胜
                dp[mouse][cat][turn] = CAT_WIN;
            } else {    // 双方不能直接获胜,递归寻找下一次移动的状态
                getNextResult(mouse, cat, turn);
            }
        }
        // 返回当前位置的状态
        return dp[mouse][cat][turn];
    }

    public void getNextResult(int mouse, int cat, int turn) {
        // turn 为偶数时老鼠移动,奇数时猫猫移动
        int move = turn % 2 == 0 ? mouse : cat;
        // 初始化的状态
        int defaultResult = move == mouse ? CAT_WIN : MOUSE_WIN;
        // 最终状态
        int result = defaultResult;
        // 遍历下一次移动可能有的状态
        for (int next : graph[move]) {
            // 如果是猫猫移动,不能到节点 0
            if (move == cat && next == 0) {
                continue;
            }
            // 移动后猫猫和老鼠的位置
            int nextMouse = move == mouse ? next : mouse;
            int nextCat = move == cat ? next : cat;
            // 移动到新位置可能有的状态
            int nextResult = getResult(nextMouse, nextCat, turn + 1);
            // 如果新状态不是对应移动方的必败状态,更新
            if (nextResult != defaultResult) {
                result = nextResult;
                // 如果不是平局状态,代表移动方获胜,跳出循环
                if (result != DRAW) {
                    break;
                }
            }
        }
        // 更新当前位置的状态
        dp[mouse][cat][turn] = result;
    }
}

你可能感兴趣的:(动态规划,leetcode,动态规划,算法)