知道SG函数是干什么的

          看了几天博弈论,今天突然豁然开朗,充分理解了SG函数,网上有讲得很好的博弈,三种基本博弈在看完后应该会理解,但是对于理解SG函数,这个链接讲的很

不错,(http://www.cnitblog.com/weiweibbs/articles/42735.html),看第一遍的时候,没人可以看的很明白,所以一定要自己在纸上根据其定义算出几个数的sg值,

例如可选步数集合为{1, 2, 3},算出0到8的sg值,结果如下:

x                0    1    2    3    4    5    6    7    8

g(x)           0    1    2    3    0    1    2    3    0

          然后根据计算结果,将上面的那个博客链接里的这段话(首先,所有的terminal position所对应的顶点,也就是没有出边的顶点,其SG值为0,因为它的后继集合是

空集。然后对于一个g(x)=0的顶点x,它的所有后继y都满足g(y)!=0。对于一个g(x)!=0的顶点,必定存在一个后继y满足g(y)=0)挨个字的看,看完这段话就会恍然大悟,知

道sg函数到底是干什么的。然后我想说,上面那个计算的例子其实就是巴什博奕用SG函数来做,他的SG值就是n%(k+1);

          理解了SG函数后,对于组合博弈就可以解决了,但一定要记住那句话(游戏的和的SG函数值就是它的所有子游戏的SG函数值的异或),这句话很重要,也是SG的

精髓,知道这个后就可以解决下面两道关于SG函数的题了;


poj 2311 链接  http://acm.hust.edu.cn/vjudge/problem/17242

题目大意就是说给定一个矩形纸片,然后每个人轮流剪,谁最后剪到1*1就胜利,显然最终状态为(2,2)(2,3)(3,2)时为必输态,所以根据矩形长宽计算其SG值,

然后判断状态就可以了。代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

int sg[205][205];
int vis[205];

int Sg(int n, int m)
{
    if(sg[n][m] != -1)
        return sg[n][m];
    memset(vis, 0, sizeof(vis));
    for(int i=2; i<=n-i; ++i)
    {
        vis[(Sg(i, m) ^ Sg(n-i, m))] = 1;
    }
    for(int i=2; i<=m-i; ++i)
    {
        vis[(Sg(n, i) ^ Sg(n, m-i))] = 1;
    }
    for(int i=0; i<205; ++i)
        if(!vis[i])
        {
            sg[n][m] = i;
            return i;
        }
}
int main()
{
    int w, h;
    memset(sg, -1, sizeof(sg));
    sg[2][2] = sg[2][3] = sg[3][2] = 0;
    while(scanf("%d%d", &w, &h)!=EOF)
    {
        if(Sg(w, h))
            printf("WIN\n");
        else
            printf("LOSE\n");
    }

    return 0;
}

还有一道类似的题目,hdu 5795 链接:

题目大意,两个人玩改进版的尼姆博弈游戏,在原先的基础上,某个人可以将一堆石子分为非空的三堆,然后判断输赢,这道题可以考虑在一堆的情况下的SG值,打表后

会发现规律,打表代码如下

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;

int sg[110];
bool vis[1100];

int Sg(int x)
{
    memset(vis, 0, sizeof(vis));
    for(int i=0; i<x; i++)
    {
        vis[sg[i]] = true;
    }
    int ans;
    for(int i=1; i<x; i++)
    {
        for(int j=1; j+i<x; j++)
        {
            vis[sg[i]^sg[j]^sg[x-i-j]] = true;
        }
    }

    for(int i=0; i<=1000; i++)
        if(!vis[i])
            return i;
}
int main()
{
    memset(sg,-1,sizeof(sg));
    sg[0] = 0;

    for(int i=1; i<=100; i++)
    {
        sg[i] = Sg(i);
    }
    for(int i=0; i<=100; i++)
    {
        printf("sg[%d] = %d\n", i, sg[i]);
    }
    return 0;
}

ac代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

int main()
{
    int T, n, t;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &n);
        int sum = 0;
        for(int i=1; i<=n; ++i)
        {
            scanf("%d", &t);
            if(t%8 == 0) t--;
            else if(t%8 == 7) t++;
            sum ^= t;
        }
        if(sum)
            printf("First player wins.\n");
        else
            printf("Second player wins.\n");
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值