题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4678
题目大意:在N*M的方格中,有K个旗子,每个方格记录周围8个方格中旗子个数的数目。0代表空方格。
两个玩家轮流进行三个操作:
点击空格方格,空格方格消失并且每个空方格连接的数字方格也消失
点击数字方格,数字方格消失
如果点击到旗子方格,游戏结束,胜负可以判定
问:两个玩家都采取最优策略,先手是否必胜?第一次点击到旗子的玩家失败
思路:考虑SG定理和SG函数
首先,最初局面是N*M个方格的最初状态,子游戏可以分为两种
1.单个数字方格的点击游戏
2.单个空白方格和一系列数字方格的点击游戏
(1)由SG定理可知,整个状态的SG值由上述两组游戏的SG值异或运算可以得到
(2)考虑单个数字方格的SG值:点击之后为空必败,因此SG(x)=0,x是空状态,也是单个数字方格的后继状态
所以由SG函数的定义可知:SG(点击单个数字方格游戏)=1
(3)重点考虑点击单个空白方格后连接的空白方格也消失并且连接的数字方格消失的情况
我们同样将这种游戏状态分开考虑,由于点击任意空白方格的影响是一样的(全消失),我们简化为一个空白方格,假设有n个数字方格相连,下面进行分析:
a.n==0的时候,点击后必败因此SG==1
b.n==1的时候,点击空格方格必败,点击数字方格转化上述为SG==1的情况,因此SG=2
c.n==2的时候,点击空格方格必败,点击数字方格转化为上述SG==2的情况,因此SG=1
d...综上所述,有一个规律:数字方格数目为n时,整个游戏的SG=n%2+1
(4)根据上述理论,我们可以用bfs把每个空格连通区域的SG值求出后,再和单个数字方格的SG值异或
最后得到整个游戏最初局面的SG值
AC代码如下:
#include
using namespace std;
const int maxn = 1e3 + 10;
struct node
{
int x,y;
};
int a[maxn][maxn];
int n,m,t,k;
int x,y,dx,dy;
int num;
bool vis[maxn][maxn];
int dir[8][2] = {{1,1},{1,-1},{1,0},{0,1},{0,-1},{-1,1},{-1,-1},{-1,0}};
int bfs(int i,int j)
{
int res = 1,di,dj;
queueque;
node cur,nex;
cur.x = i;
cur.y = j;
que.push(cur);
vis[i][j] = 1;
while(!que.empty())
{
node cur = que.front();
que.pop();
for(int k = 0; k < 8; ++k)
{
di = cur.x + dir[k][0];
dj = cur.y + dir[k][1];
if(di < 0||di >= n||dj < 0||dj >= m)
continue;
if(vis[di][dj])
continue;
if(a[di][dj] == 0)
{
nex.x = di;
nex.y = dj;
que.push(nex);
}
else if(!vis[di][dj])
res++;
vis[di][dj] = 1;
}
}
return res;
}
int main()
{
int cas = 1;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
memset(a,0,sizeof(a));
scanf("%d%d%d",&n,&m,&k);
while(k--)
{
scanf("%d%d",&x,&y);
a[x][y] = -1;
for(int i = 0; i < 8; ++i)
{
dx = x + dir[i][0];
dy = y + dir[i][1];
if(dx < 0||dx >= n||dy < 0||dy >= m)
continue;
if(a[dx][dy] == -1)
continue;
a[dx][dy] = 1;
}
}
int res = 0;
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
if(a[i][j] == 0 && !vis[i][j])
{
num = bfs(i,j);
if(num % 2 == 0)
res ^= 2;
else
res ^= 1;
}
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
if(!vis[i][j] && a[i][j] == 1)
res ^= 1;
printf("Case #%d: ",cas++);
if(res == 0)
printf("Fanglaoshi\n");
else
printf("Xiemao\n");
}
return 0;
}