HDU 5724 Chess 从懵逼到学会 SG函数

来源:互联网 发布:数据挖掘招聘 编辑:程序博客网 时间:2024/05/17 02:59

引入

经典的NIM博弈

给若干堆石子,Alice和Bob轮流取石子,每次可选择其中的一堆拿走任意多的石子,但不能不拿。最后没有石子可拿的人输。
已知:堆数N,每堆石子数目ai,Alice先取。
求:谁是胜者。

博弈基于足够聪明的两者,每次都尽量取到必胜的状态,绝不存在其中一人明明可以胜出却要让别人赢的情况。因此输赢往往由局面本身确定。即存在必胜态必败态

上面这个游戏的必败态,即玩家面对的是没有任何石子的情况。

能够经过一步操作存在必败态的状态,就一定是必胜态
经过一步操作全是必胜态的状态,一定是必败态

可以发现
a1a2...an=1为必胜态
a1a2...an=0为必败态

MEX函数

MEX的运算对象为一个集合S,MEX(S)即最小的不属于S的非负整数。如

S MEX(S) ϕ 0 {1,2,3} 0 {0,1,4} 2

定义

对于一个给定的有向无环图,图上每个点表示一种局面或状态。
定义关于图的每个顶点的SG函数如下

SG(x)=MEX{SG(y)|xy}

y,SG(y)=0,SG(x)0
y,SG(y)0,SG(x)=0

这个描述和上面的必胜必败态完全一致,可见当SG(x)=0时,x即为一个必败态。

若有若干个平行局面共同组成了一个游戏,则这个父局面的状态由子局面的SG值决定

SG(a1)SG(a2)...SG(an)=0


应用

终于到了做题环节!

HDU 5724 Chess

今年的第一把多校,结果三个人都没看这题(过题人数第二多的样子T.T)

题意:n×20的棋盘内放置了一些棋子,每颗棋子走到向右的最近的空格为一次操作,Alice和Bob轮流下棋,无路可走的就输。Alice先手。问Alice会不会输呢?

每行都是独立的,只要计算出每行状态的SG函数值,异或即得到答案。
一行中若有x个棋子,则它可以到达的状态y即有x个,通过SG(x)=MEX{SG(y)|xy}打表。

通过x找到y的过程,可以用数组模拟,但是又慢又蠢啊。其实交换0和1之后其余位的和没有变,可以节省一半的时间。先找1再找后面最近的0,和先找0再找前面连续的1,时间也差很多啊。不过出题人还是良心的,最慢的重复算也没有超时(险过…)

附上优雅的打表…

for(int i = 1;i < (1<<20); i++){    int h[25];    memset(h, -1, sizeof(h));    int last = -1;    for(int j = 0; j < 20; j++){        if(!((i >> j) & 1))            last = j;        if(((i >> j) & 1)){            if(last != -1){                h[sg[(i ^ (1 << j)) ^ (1 << last)]]=1;            }        }    }    int j=0;    while(h[j] != -1) j++;    sg[i]=j;}
0 0
原创粉丝点击