【博弈】移棋子游戏
Description
给定一个有N个节点的有向无环图,图中某些节点上有棋子,两名玩家交替移动棋子。
玩家每一步可将任意一颗棋子沿一条有向边移动到另一个点,无法移动者输掉游戏。
对于给定的图和棋子初始位置,双方都会采取最优的行动,询问先手必胜还是先手必败。
玩家每一步可将任意一颗棋子沿一条有向边移动到另一个点,无法移动者输掉游戏。
对于给定的图和棋子初始位置,双方都会采取最优的行动,询问先手必胜还是先手必败。
Input
第一行,三个整数N、M、K, N表示图中节点总数,M表示图中边的条数,K表示棋子的个数
接下来M行,每行两个整数X,Y表示有一条边从X出发指向Y。
接下来一行,K个空格间隔的整数,表示初始时,棋子所在的节点编号
接下来M行,每行两个整数X,Y表示有一条边从X出发指向Y。
接下来一行,K个空格间隔的整数,表示初始时,棋子所在的节点编号
Output
若先手胜,输出"win",否则输出"lose"
Sample Input
6 8 4
2 1
2 4
1 4
1 5
4 5
1 3
3 5
3 6
1 2 4 6
Sample Output
win
Hint
N<=2000,M<=6000,1<=K<=N
Solution
一道博弈题。SG函数的模板。
值得注意的就是博弈的一些定理了,但是本蒟蒻现在都还没有弄明白,所以也就不能说出原理了。
读入数据并建图之后,我们将每一个出度为0的点的SG函数的值设置为0(别问我为什么,和某个神秘的mex函数有关)。
相应的,我们可以用dfs的方式的出每一个点的SG函数值。最后整个游戏的SG函数值就是每一个棋子的SG值的异或和。(SG定理)。当SG(G)=0时,先手必败。
下面附上代码
CODE
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
struct Road{int next,to;}road[10005];
int h[2005],cnt=0;
inline void add(int x,int y){road[++cnt].to=y;road[cnt].next=h[x];h[x]=cnt;return ;}
int prep[2005];
int SG[2005];
int st[2005];
int mark[2005];
int judge[2005];
int N,M,K;
int ans;
inline void Make_SG(int v){
if(v==0)return ;
memset(judge,0,sizeof(judge));
int i,j;
for(i=h[v];i;i=road[i].next){
j=road[i].to;
judge[SG[j]]=1;
}
for(i=0;i<=N;i++){
if(judge[i]==0){
SG[v]=i;break;
}
}
Make_SG(prep[v]);
return ;
}
int main(){
cin>>N>>M>>K;
int i,j,x,y;
for(i=1;i<=M;i++){
cin>>x>>y;
add(x,y);
prep[y]=x;mark[x]=1;
}
for(i=1;i<=N;i++){
if(mark[i]==0){
SG[i]=0;Make_SG(prep[i]);
}
}
ans=0;
for(i=1;i<=K;i++){
cin>>x;
ans=ans^SG[x];
}
if(ans)cout<<"win";
else cout<<"lose";
return 0;
}