博弈论入门总结

NIM游戏

根据游戏规则无法再进行行动者为负。
就是将各个子游戏的sg值进行异或,不为零则初始为N态(先手必胜)。比如抓石子游戏,sg[i]=i。

S-NIM游戏

取走最后一个石子者为负。(终止态为N态)
先手必胜当且仅当:
1.所有堆的石子数都为1,且游戏的SG值为0(此时退化为简单的奇偶处理)
2.存在石子数大于1的堆,且游戏的SG值不为0

入门例题:LightOJ 1253 Misere NIM
http://lightoj.com/volume_showproblem.php?problem=1253

#include <iostream>
#include <cstdio>

using namespace std;
int a[105];
int main()
{
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++)
    {
        int ans=0;
        int flag=0;
        int k;
        scanf("%d",&k);
        for(int i=0;i<k;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>1)
                flag=1;
        }
        for(int i=0;i<k;i++)
            ans^=a[i];
        if((ans&&flag)||(!flag&&!ans))
            printf("Case %d: Alice\n",tt);
        else
            printf("Case %d: Bob\n",tt);
    }
    return 0;
}

SG函数

关于SG函数,大佬的文章讲的非常清楚
https://blog.csdn.net/strangedbly/article/details/51137432

关于SG函数的入门题目:
LightOJ 1296 Again Stone Game
http://lightoj.com/volume_showproblem.php?problem=1296
还是取石子游戏,只是加上一个限制,每次最多不能超过该堆石子总数的一半(向下取整)
所以显然,石子数为1时是一个P态(无法取了)。数量=2时只能取一个,他的后继状态就是数量=1,所以SG[2]=mex(SG[1])=1。同理,SG[3]=mex(SG[2])=0。
因为数据量很大(1e9)没法打表,所以找下规律。
多推几个就会发现,数量i是偶数时,SG[i]=i/2;数量i是奇数时,SG[i]=SG[i/2]。
代码:

#include <iostream>
#include <cstdio>

using namespace std;

int sg(int x)
{
    if(x==1||x==0)
        return 0;
    if(x%2==0)
        return x/2;
    else
        return sg(x/2);
}

int main()
{
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++)
    {
        int k,ans=0;
        scanf("%d",&k);
        for(int i=0;i<k;i++)
        {
            int ai;
            scanf("%d",&ai);
            ans^=sg(ai);
        }
        if(ans)
            printf("Case %d: Alice\n",tt);
        else
            printf("Case %d: Bob\n",tt);
    }
    return 0;
}

LightOJ 1315 Game of Hyper Knights
http://lightoj.com/volume_showproblem.php?problem=1315
可以说是SG打表的模板题了。
左上角的点肯定是P态,从左上角开始打表,每个点的SG值就是mex{6个后继状态}。
因为计算当前点时会用到后继点的SG,所以打表时需要斜着打。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 505

using namespace std;

int sg[N][N];
int Hash[N];

void getsg()
{
    for(int i=0;i<N*2;i++)
        for(int j=0;j<=i/2;j++)
        {
            if(i-j<N)
            {
                memset(Hash,0,sizeof(Hash));
                if(j-2>=0&&i-j-1>=0)
                    Hash[sg[j-2][i-j-1]]=1;
                if(j-3>=0&&i-j-1>=0)
                    Hash[sg[j-3][i-j-1]]=1;
                if(j-1>=0&&i-j-2>=0)
                    Hash[sg[j-1][i-j-2]]=1;
                if(j-1>=0&&i-j-3>=0)
                    Hash[sg[j-1][i-j-3]]=1;
                if(j+1<N&&i-j-2>=0)
                    Hash[sg[j+1][i-j-2]]=1;
                if(j-2>=0&&i-j+1<N)
                    Hash[sg[j-2][i-j+1]]=1;
                for(int k=0;k<N;k++)
                    if(!Hash[k])
                    {
                        sg[j][i-j]=sg[i-j][j]=k;
                        break;
                    }
            }
        }
}

int main()
{
    getsg();
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++)
    {
        int n;
        scanf("%d",&n);
        int ans=0;
        for(int i=0;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            ans^=sg[a][b];
        }
        if(ans)
            printf("Case %d: Alice\n",tt);
        else
            printf("Case %d: Bob\n",tt);
    }
    return 0;
}

lightoj 1199 Partitioning Game
http://lightoj.com/volume_showproblem.php?problem=1199
有n堆石子,每次选一堆将其分成数量不等的两堆。不能分的人输。
显然,1和2是P态,SG=0。
3可以分成1和2,然后对手无法分割,是N态;4只能分成1和3,然后对手把3分成1和2,自己无法分割,所以4是P态。这两个比较好推,因为3和4都只有一个后继状态。
对于5,可以分成1+4和2+3,根据SG值的定义,
应该是求mex{SG[后继a(1+4)] , SG[后继b(2+3)]}。
对于1+4的SG值的求法,应该是SG[1] ^ SG[4]。因为将1+4这个状态单拿出来看成一个新游戏,这个子游戏的SG值就是SG[1] ^ SG[4],而SG[1]和SG[4]都已经求出,所以可以这样通过前面的SG值一步步推出全部的SG值。
所以SG[i]=mex{SG[1] ^ SG[i-1] , SG[2] ^ SG[i-2] , … , SG[j] ^ SG[i-j]} (1<=j<(i+1)/2)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#define N 10005

using namespace std;

int sg[N],Hash[N];

void getsg()
{
    for(int i=3;i<N;i++)
    {
        memset(Hash,0,sizeof(Hash));
        for(int j=1;j<(i+1)/2;j++)
            Hash[sg[j]^sg[i-j]]=1;
        for(int j=0;j<N;j++)
            if(!Hash[j])
            {
                sg[i]=j;
                break;
            }
    }
}

int main()
{
    getsg();
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++)
    {
        int ans=0;
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            int ai;
            scanf("%d",&ai);
            ans^=sg[ai];
        }
        if(ans)
            printf("Case %d: Alice\n",tt);
        else
            printf("Case %d: Bob\n",tt);
    }
    return 0;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值