1733. 灭鼠先锋 II

对给定局面,求后手是否必胜。由于 2^{20}\approx 10^6 ,遍历全部状态是可行的,且状态转移数不多(每个状态的转移数最多为 nm+n(m-1) ),所以是一个稀疏图,可以考虑记忆化搜索,可以用二进制状态压缩存储每个局面的搜索结果,时间复杂度为 O(nm2^{n+m}) ,空间复杂度为 O(2^{n+m}) 。根据博弈论局面的性质直接DFS即可。

#include <bits/stdc++.h> using namespace std; #define sc(x) scanf("%lld", &x) typedef long long ll; #define mn 1048580 // 2^{20}+4 #define N 0 #define P 1 ll n, m, t, s[mn]; // s[si]=N(0)先手必胜;s[si]=P(1)先手必败 #define O 0 #define X 1 struct state { bool a[22][22]; ll toi() //把a转化为二进制状态 { ll r = 0; for (ll i = 1, k = 1; i <= n; ++i) { for (ll j = 1; j <= m; ++j, k *= 2) { r += k * a[i][j]; } } return r; } } now; ll dfs() { ll si = now.toi(); if (s[si] != -1) //记忆化剪枝 { return s[si]; } ll hasP = 0, numx = 0; //是否可以移动到P for (ll i = 1; i <= n; ++i) { for (ll j = 1; j <= m; ++j) { numx += now.a[i][j] == X; if (now.a[i][j] == O) { now.a[i][j] = X; hasP |= dfs(); now.a[i][j] = O; //回溯 } if (j < m && now.a[i][j] == O && now.a[i][j + 1] == O) { now.a[i][j] = now.a[i][j + 1] = X; hasP |= dfs(); now.a[i][j] = now.a[i][j + 1] = O; } } } s[si] = hasP ? N : (numx == n * m ? N : P); return s[si]; //一般的ICG就hasP?N:P就行了 } char ans[] = "LV", q[22][22]; //先手N(0)=V,P(1)=L,先手VL;故后手LV signed main() { for (ll i = 0; i < mn; ++i) { s[i] = -1; //表示未知 } sc(n), sc(m); dfs(); for (sc(t); t--;) { for (ll i = 1; i <= n; ++i) { scanf("%s", q[i] + 1); } for (ll i = 1; i <= n; ++i) { for (ll j = 1; j <= m; ++j) { now.a[i][j] = q[i][j] == 'O' ? O : X; } } printf("%c\n", ans[s[now.toi()]]); } return 0; }