You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
5.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package class_2022_01_1_week;
import java.util.ArrayList;
import java.util.Arrays;
// 测试链接 : https://leetcode.com/problems/cat-and-mouse/
public class Code02_CatAndMouse {
// 不贪心,就递归 + 记忆化搜索
public static int catMouseGame1(int[][] graph) {
int n = graph.length;
// 9 : 2 * 8 * 9 再大,平局
int limit = ((n * (n - 1)) << 1) + 1;
int[][][] dp = new int[n][n][limit];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Arrays.fill(dp[i][j], -1);
}
}
return process(graph, limit, 2, 1, 1, dp);
}
// 贪心 + 递归 + 记忆化搜索
// 但是不对!
// 再次强调!不要去证明!
public static int catMouseGame2(int[][] graph) {
int n = graph.length;
// 这里!
// int limit = (n << 1) + 2; 还会出错,但是概率很小,需要多跑几次
// int limit = (n << 1) + 3; 就没错了,或者说,概率小到很难重现
// 为啥?我屌你为啥!
// 讲了这节课之后leetcode又增加了测试用例
// 导致 int limit = (n << 1) + 3; 也会出错
// 改成如下的+12可以通过但是如果去人为构造错误用例一定还会出现错误
int limit = (n << 1) + 12;
int[][][] dp = new int[n][n][limit];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
Arrays.fill(dp[i][j], -1);
}
}
return process(graph, limit, 2, 1, 1, dp);
}
// dp[][][] 傻缓存!
// dp[cat][mouse][turn] == -1 这个状态之前没算过!
// dp[cat][mouse][turn] == 0 这个状态之前算过!平局!
// dp[cat][mouse][turn] == 1 这个状态之前算过!老鼠赢!
// dp[cat][mouse][turn] == 2 这个状态之前算过!猫赢!
// 固定参数轮数不要超过limit如果超过就算平局
public static int process(int[][] graph, int limit, int cat, int mouse, int turn, int[][][] dp) {
if (turn == limit) {
return 0;
}
if (dp[cat][mouse][turn] != -1) {
return dp[cat][mouse][turn];
}
int ans = 0;
if (cat == mouse) {
ans = 2;
} else if (mouse == 0) {
ans = 1;
} else {
if ((turn & 1) == 1) { // 老鼠回合
ans = 2;
for (int next : graph[mouse]) {
int p = process(graph, limit, cat, next, turn + 1, dp);
ans = p == 1 ? 1 : (p == 0 ? 0 : ans);
if (ans == 1) {
break;
}
}
} else { // 猫回合
ans = 1;
for (int next : graph[cat]) {
if (next != 0) {
int p = process(graph, limit, next, mouse, turn + 1, dp);
ans = p == 2 ? 2 : (p == 0 ? 0 : ans);
if (ans == 2) {
break;
}
}
}
}
}
dp[cat][mouse][turn] = ans;
return ans;
}
// 为了测试
// 暴力尝试
public static int right(int[][] graph) {
int n = graph.length;
boolean[][][] path = new boolean[n][n][2];
return win(graph, 2, 1, 1, path);
}
// 暴力尝试
// 返回值int
// 0 : 平局
// 1 : 老鼠赢
// 2 : 猫赢
// int[][] graph
// 0 : {3, 7, 9}
// 1 :
// 2 :
// 3 : {0, }
// ...
// 7 : {0, }
// ...
// 9 : {0, }
// 猫此时的位置 -> cat
// mouse
// turn == 1 老鼠的回合 turn == 0 猫的回合
// 当第一次出现在老鼠的回合猫在5位置 老鼠在7位置
// path[cat][mouse][1] = false
// 不是第一次出现 path[cat][mouse][1] = true
public static int win(int[][] graph, int cat, int mouse, int turn, boolean[][][] path) {
if (path[cat][mouse][turn]) { // 之前来到过这个状态!平局!
return 0;
}
// 没来过!
path[cat][mouse][turn] = true;
int ans = 0;
if (cat == mouse) {
ans = 2;
} else if (mouse == 0) {
ans = 1;
} else { // 游戏继续
if (turn == 1) { // 老鼠回合
// 最差情况,是猫赢!
ans = 2;
for (int next : graph[mouse]) {
int p = win(graph, cat, next, 0, path);
// p的返回值如果是1代表老鼠赢那么最差结果ans = 1直接返回
ans = p == 1 ? 1 : (p == 0 ? 0 : ans);
if (ans == 1) {
break;
}
}
} else { // 猫回合
ans = 1;
for (int next : graph[cat]) {
if (next != 0) {
int p = win(graph, next, mouse, turn ^ 1, path);
ans = p == 2 ? 2 : (p == 0 ? 0 : ans);
if (ans == 2) {
break;
}
}
}
}
}
path[cat][mouse][turn] = false;
return ans;
}
// 为了测试
public static int[][] randomGraph(int n) {
ArrayList<ArrayList<Integer>> g = new ArrayList<>();
for (int i = 0; i < n; i++) {
g.add(new ArrayList<>());
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (Math.random() > 0.5) {
g.get(i).add(j);
g.get(j).add(i);
}
}
}
int[][] graph = new int[n][];
for (int i = 0; i < n; i++) {
int m = g.get(i).size();
graph[i] = new int[m];
for (int j = 0; j < m; j++) {
graph[i][j] = g.get(i).get(j);
}
}
return graph;
}
// 为了测试
public static void main(String[] args) {
System.out.println("证什么证!?对数器万岁!");
int N = 7;
int testTime = 3000000;
System.out.println("测试开始");
for (int i = 0; i < testTime; i++) {
int n = (int) (Math.random() * N) + 3;
int[][] graph = randomGraph(n);
int ans1 = catMouseGame1(graph);
int ans2 = catMouseGame2(graph);
if (ans1 != ans2) {
for (int row = 0; row < graph.length; row++) {
System.out.print(row + " : ");
for (int next : graph[row]) {
System.out.print(next + " ");
}
System.out.println();
}
System.out.println("ans1 : " + ans1);
System.out.println("ans2 : " + ans2);
System.out.println("出错了!");
break;
}
}
System.out.println("测试结束");
// 给你记录一个错误的例子
int[][] graph = {
// 0 :
{ 2, 6, 7 },
// 1 :
{ 3, 4, 5, 7 },
// 2 :
{ 0, 3, 4, 7 },
// 3 :
{ 1, 2, 5, 6 },
// 4 :
{ 1, 2, 5, 7 },
// 5 :
{ 1, 3, 4, 6 },
// 6 :
{ 0, 3, 5, 7 },
// 7 :
{ 0, 1, 2, 4, 6 } };
System.out.println(catMouseGame1(graph));
System.out.println(catMouseGame2(graph));
}
}