怀旧游戏 题解(逆向bfs打表博弈)
题目链接
题目思路
一直不太会这种有环的博弈打表,今天学到了新知识
处理出每个点的前驱节点,找出所有必败点,然后转移,确定状态后再放入队列,太秒了
用 \(f[i][j][k][l]\) 表示先手手上的数字是 \(i, j\),后手手上的数字是 \(k, l\) 时,先手的胜负情况。初始先手必败的情况比较好处理(\(k\) 或 \(l\) 是 0,意味着先手已经输了),接着反向 bfs,对于一个状态,如果它存在一个先手必败的后续状态,则这个状态先手必胜;如果它所有的后续状态都是先手必胜的,则这个状态先手必败。
对于无法确定胜负的状态,都是平局。
直接放官方题解和官方代码
代码
#include <bits/stdc++.h>
using namespace std;
int f[11][11][11][11], c[10001][4], b[11][11][11][11];
int test, a1, a2, b1, b2;
struct node {
int a1, a2, b1, b2;
};
vector<node> a[11][11][11][11];
int main() {
for (int i = 1; i <= 9; i++)
for (int j = 1; j <= 9; j++)
for (int k = 1; k <= 9; k++)
for (int l = 1; l <= 9; l++) {
node res;
res.a1 = i; res.a2 = j;
res.b1 = k; res.b2 = l;
a[k][l][(i + j) % 10][j].push_back(res);
a[k][l][(i + k) % 10][j].push_back(res);
a[k][l][(i + l) % 10][j].push_back(res);
a[k][l][i][(j + i) % 10].push_back(res);
a[k][l][i][(j + k) % 10].push_back(res);
a[k][l][i][(j + l) % 10].push_back(res);
}
memset(f, 0, sizeof(f));
int head = 0;
for (int i = 1; i <= 9; i++)
for (int j = 1; j <= 9; j++)
for (int k = 1; k <= 9; k++) {
f[i][j][k][0] = 2;
c[++head][0] = i; c[head][1] = j;
c[head][2] = k; c[head][3] = 0;
f[i][j][0][k] = 2;
c[++head][0] = i; c[head][1] = j;
c[head][2] = 0; c[head][3] = k;
}
for (int l = 1; l <= head; l++) {
int a1 = c[l][0], a2 = c[l][1], b1 = c[l][2], b2 = c[l][3];
int v = f[a1][a2][b1][b2];
if (v == 2) {
for (auto i : a[a1][a2][b1][b2])
if (!f[i.a1][i.a2][i.b1][i.b2]) {
//printf("!\n");
f[i.a1][i.a2][i.b1][i.b2] = 1;
c[++head][0] = i.a1; c[head][1] = i.a2;
c[head][2] = i.b1; c[head][3] = i.b2;
}
} else {
for (auto i : a[a1][a2][b1][b2])
if (!f[i.a1][i.a2][i.b1][i.b2]
&& ++b[i.a1][i.a2][i.b1][i.b2] == 6) {
f[i.a1][i.a2][i.b1][i.b2] = 2;
c[++head][0] = i.a1; c[head][1] = i.a2;
c[head][2] = i.b1; c[head][3] = i.b2;
}
}
}
scanf("%d", &test);
for (; test--; ) {
scanf("%d%d%d%d", &a1, &a2, &b1, &b2);
if (f[a1][a2][b1][b2] == 1)
printf("Alice\n");
else
if (f[a1][a2][b1][b2] == 2)
printf("Bob\n");
else
printf("Tie\n");
}
}
不摆烂了,写题