Description
在一个有向无环图上放着n个棋子,Alice和Bob每次可以选择其中一枚棋子沿有向边移动到任意一个后继点(一个点上可以同时有多枚棋子),最终不能移动者输。问如果两人都足够机智,先手Alice是否有必胜策略
Input
多组输入,每组用例第一行为图中点数n(点的标号从0~n-1),之后n行每行首先输入一个整数m表示与该点相连的点数,之后m个整数表示这m个点的下标,在每张图输入完毕后会有多组游戏,每组游戏占一行,每组游戏首先输入棋子个数m,之后m个整数表示这m枚棋子的位置,游戏的输入以m=0结束,以文件为结束全部输入
Output
对于每组游戏,如果先手有必胜策略则输出WIN,否则输出LOSE
Sample Input
4
2 1 2
0
1 3
0
1 0
2 0 2
0
4
1 1
1 2
0
0
2 0 1
2 1 1
3 0 1 3
0
Sample Output
WIN
WIN
WIN
LOSE
WIN
Solution
经典S-Nim游戏,这类博弈游戏的突破口往往在sg值的定义上,首先解决一枚棋子后即可解决多枚棋子,因为只要求出每枚棋子初始位置的sg值之后取异或判断是否为0即可,此题对于没有后继的点,定义其sg值为0,对于有后继的点,定义其sg值为其所有后继点sg值中没有出现过的最小自然数,这样一遍dfs即可求出所有点的sg值
Code
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define maxn 1111
int M[maxn][maxn];
int n;
int sg[maxn];
int dfs(int x)
{
if(sg[x]!=-1)
return sg[x];
bool zrs[maxn];//自然数集合
memset(zrs,false,sizeof(zrs));//初始化
for(int i=0;i<n;i++)
if(M[x][i]==1)//将子节点的sg值标记
zrs[dfs(i)]=true;
for(int i=0;;i++)//找到其子节点sg值中没出现过的最小自然数即为该点sg值
if(zrs[i]==0)
return sg[x]=i;
}
int main()
{
while(~scanf("%d",&n))
{
//初始化
memset(M,-1,sizeof(M));
memset(sg,-1,sizeof(sg));
for(int i=0;i<n;i++)
{
int x,y;
scanf("%d",&x);
if(x==0)//没有出度的点sg值为0
sg[i]=0;
else
{
while(x--)
{
scanf("%d",&y);
M[i][y]=1;
}
}
}
int m;
while(scanf("%d",&m),m)
{
int ans=0;
while(m--)
{
int q;
scanf("%d",&q);
ans^=dfs(q);
}
if(ans!=0) printf("WIN\n");
else printf("LOSE\n");
}
}
}