题目来源: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;
}
}