博弈之sg函数 A chess game

来源:互联网 发布:翻车鱼能吃吗 知乎 编辑:程序博客网 时间:2024/06/14 01:41

题目大意:

有一个有N个顶点的有向无环图,顶点上有棋子,两个人,每次根据这个图移动任意一个棋子走一步

最后不能移动棋子的为Lose,问先手的玩家是否能获胜

 

题目分析:

根据大意,很容易看出来是博弈的题目,这次用了SG函数来求解,

SG函数大致简单的学习可以在百度百科有:http://baike.baidu.com/view/2855458.htm

重点分解成子问题 和 求出子问题中每一个状态点的SG值!

 

对于一个给定的有向无环图,定义关于图的每个顶点的Sprague-Grundy函数g如下:g(x)=mex{g(y) | y是x的后继 }

mex(minimalexcludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

来看一下SG函数的性质。首先,所有的terminalposition所对应的顶点,也就是没有出边的顶点,其SG值为0,因为它的后继集合是空集。然后对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0。

其实细细想一下,这样的SG分析就有点像PN分析了!

 

这一题,图上有很多个点,每一个点在图上的SG值就是一个子问题,然后把每个子问题的SG值异或起来,就是问题的解

在网上学得求SG值的方法大致都是递归的方法

#include<stdio.h>

#include<string.h>

intmap[1010][1010];

intsg[1010];

int N;

int DFS(intn)//可以作为模板来参考

{

int i;

if(sg[n]!=-1)return sg[n];

intflag[1010];

memset(flag,0,sizeof(flag));

for(i=0;i<N;i++)

{

if(map[n][i]!=-1)

flag[DFS(i)]=1;

}

i=0;

while(flag[i])

i++;

returnsg[n]=i;

}

intmain()

{

inti,j,k,t;

int X;

inttemp,ans;

while(scanf("%d",&N)!=EOF)

{

memset(map,-1,sizeof(map));

memset(sg,-1,sizeof(sg));

for(i=0;i<N;i++)

{

scanf("%d",&k);

if(k==0)

{

sg[i]=0;

}

for(j=0;j<k;j++)

{

scanf("%d",&t);

map[i][t]=1;

}

}

while(scanf("%d",&X)!=EOF)

{

if(X==0)

break;

ans=0;

for(i=0;i<X;i++)

{

scanf("%d",&temp);

ans^=DFS(temp);

}

if(ans!=0)

{

printf("WIN\n");

}

else

{

printf("LOSE\n");

}

}

}

return0;

}