1. 题目来源
2. 题目解析
棋盘类博弈论问题大多数采用记忆化搜索来进行优化。有效的状态空间没那么多,大多数都 break
或者 return
掉了。
思路:
- 理解最优策略下猫和老鼠两者的 必胜态和必败态 的意思。
- 这里的
k
不能入题取到最大限制 1000,不然妥妥超时,1 亿的计算量…在此由于采用记忆化搜索且猜测死循环应该发生在 200 步左右,故就取经验值 200 即可。 - 老鼠的必胜态转移过程中,如果发生下个状态使得老鼠和猫的位置一样,则老鼠当前状态应该是必败的,但是这种状态的无效的,因为目前进行的是必胜态的转移。老鼠也不是傻老鼠,不会自投罗网。只有当所有的必胜态均无法转移时,才会将当前状态判定为必败态,所以不能将下面的一种状态到达必败态的这种状态就将当前状态直接定义为必败态。必胜态的转移只需要一个!理解这点很重要!
- 枚举状态转移的方向和步长时需要注意下方向数组与步数的相乘即可。
博弈论问题是个挺难的知识点,稍微偏冷门,从 224 场周赛排名也能看出来,全场只 AK
了 22 位佬,所以第四题出个博弈论还是挺值得注意的。尤其是棋盘类博弈论问题,几乎记忆化搜搜成了标配!
本题也是非常经典的博弈论问题,好题!
- 时间复杂度: O ( 4 n × n 4 k ) O(4n \times n^4k) O(4n×n4k)。
- 空间复杂度: O ( n 4 k ) O(n^4k) O(n4k)
代码:
int f[8][8][8][8][200];
class Solution {
public:
int n, m, cj, mj;
vector<string> g;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dp(int cx, int cy, int mx, int my, int k) {
if (k >= 200) return 0;
auto& v = f[cx][cy][mx][my][k];
if (v != -1) return v; // 如果之前搜过 v 则直接返回
if (k & 1) { // 奇数猫走,偶数老鼠走,题目要求老鼠先走
for (int i = 0; i < 4; i ++ ) {
for (int j = 0; j <= cj; j ++ ) {
int x = cx + dx[i] * j, y = cy + dy[i] * j; // 下一个状态进行转移
if (x < 0 || x >= n || y < 0 || y >= m || g[x][y] == '#') break; // 越界或撞墙均为无效转移
if (x == mx && y == my) return v = 0; // 抓到老鼠,猫必胜
if (g[x][y] == 'F') return v = 0; // 找到食物,猫必胜
if (!dp(x, y, mx, my, k + 1)) return v = 0; // 转移状态如果为猫必胜,则当前即为猫必胜
}
}
return v = 1; // 否则猫必败
} else {
for (int i = 0; i < 4; i ++ ) {
for (int j = 0; j <= mj; j ++ ) {
int x = mx + dx[i] * j, y = my + dy[i] * j;
if (x < 0 || x >= n || y < 0 || y >= m || g[x][y] == '#') break;
if (x == cx && y == cy) continue; // 如果老鼠走到猫的位置,这个状态不要转移,最优策略下为无效状态
if (g[x][y] == 'F') return v = 1;
if (dp(cx, cy, x, y, k + 1)) return v = 1; // 转移状态如果为老鼠必胜,则当前即为老鼠必胜
}
}
return v = 0;
}
}
bool canMouseWin(vector<string>& grid, int catJump, int mouseJump) {
g = grid;
n = g.size(), m = grid[0].size(), cj = catJump, mj = mouseJump;
int cx, cy, mx, my;
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
if (g[i][j] == 'C') cx = i, cy = j;
else if (g[i][j] == 'M') mx = i, my = j;
memset(f, -1, sizeof f);
return dp(cx, cy, mx, my, 0);
}
};