题目:https://vjudge.net/contest/263079#problem/B
题目大意:两人玩游戏,棋盘上散布一些石头,谁先将其中任意一块移动到坐标(0,0)即赢,石头可以向左下角、左、下三个方向任意距离移动,求先手玩家的胜负。
这个游戏很显然可以用有向图表示,思考用sg函数求解。
但是有一些特殊情况并不能被sg表示。那就是x坐标轴、y坐标轴和x = y的轴上,这三条轴刚好符合了游戏规则,直接一发挪到(0,0)即胜。
对这种情况分先手后手探讨一下,如果一开始就出现这种石头,那先手者直接胜利。
如果先手没有这种情况,那么把石头走到这三种位置显然是愚蠢的行为…肯定不是最优策略,绝对不会走的。
因此这三种情况干脆在求sg的时候当不存在,然后特判一下就好了。
然后这题的必败态(sg = 0)则是点(1,2)和(2,1),如果走到这里,那显然就输了。
ac代码:
#include<bits/stdc++.h>
using namespace std;
int sg[105][105];
bool vis[205];
void grundy() {
for(int i = 1; i <= 100; i++) {
for(int j = 1; j <= 100; j++) {
if(i == j) {
continue;
}
memset(vis, 0, sizeof(vis));
for(int k = 1, len = max(i, j); k <= len; k++) {
int nextX = i - k;
int nextY = j - k;
if(nextX > 0 && nextX != j) {
vis[sg[nextX][j]] = 1;
}
if(nextY > 0 && nextY != i) {
vis[sg[i][nextY]] = 1;
}
if(nextX > 0 && nextY > 0 && nextX != nextY) {
vis[sg[nextX][nextY]] = 1;
}
}
int g = 0;
while(vis[g]) {
++g;
}
sg[i][j] = g;
}
}
}
int main() {
grundy();
int n, x, y, sum = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d%d", &x, &y);
if(x == y || x == 0 || y == 0){
printf("Y\n");
return 0;
}
sum ^= sg[x][y];
}
printf("%s\n", sum ? "Y" : "N");
return 0;
}