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


引入

经典的NIM博弈

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

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

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

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

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

MEX函数

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

SMEX(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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值