博弈问题总集第一类----求SG函数出解

本文解析了博弈论中SG函数的应用,通过三个经典题目——Nim游戏、S-Nim及A Chess Game,介绍了如何使用动态规划、记忆化搜索等方法来求解SG函数,并给出了详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一类是最简单的入门级别啦,这些博弈问题的大前提来自优秀的学姐
求SG函数通常用dp||记忆化搜索

1、

POJ2975 Nim

题解:

这个题的题目描述就是我们跟踪代码会看到的过程

求Nim游戏获胜的方案,其实就是求第一步可以从哪些堆里拿。可以发现 SGi (第i堆石子)假如有k个,ta的后继状态可以转移到0~k-1,那么我们根据SG函数的定义可以知道SG[i]=a[i]

这里再提一点就是k^a[i]表示总的SG值去掉这一堆石子的SG值,我们为了让总的SG值为0,就要让这一堆也就是第i堆石子的SG值为k^a[i],要想让k^a[i]在取值范围内,【根据SG函数的求法mex】,只有当(k^a[i]) < a[i]时,k^a[i]是 SGi 的后继状态,在ta的取值范围内

剩下的下方普及向见咯!

代码:

#include <cstdio>
using namespace std;
int n,a[1005];
int main()
{
    while (1)
    {
        scanf("%d",&n);if (!n) break;
        int k=0,ans=0;
        for (int i=1;i<=n;i++) scanf("%d",&a[i]),k^=a[i]; 
        for (int i=1;i<=n;i++)
          if ((k^a[i])<a[i]) ans++;
        printf("%d\n",ans);
    }
}

2、

POJ2960 S-Nim

题解:

似乎是ta怎么说你怎么做求一波SG函数就完结了?要时刻把握SG函数的性质式,这个题目描述写的特别好

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int rule[105],a[105],n,sg[10005],m;bool ext[10005];
void get_sg()
{
    memset(sg,0,sizeof(sg));

    for (int i=1;i<=10000;i++)
    {
        memset(ext,0,sizeof(ext));
        for (int j=1;j<=n;j++)
          if (i-rule[j]<0) break;
          else ext[sg[i-rule[j]]]=1;
        for (int j=0;;j++)
          if (!ext[j]) {sg[i]=j;break;}
    }
}
int main()
{
    while (1)
    {
        scanf("%d",&n); if (!n) break;
        for (int i=1;i<=n;i++) scanf("%d",&rule[i]);
        sort(rule+1,rule+n+1);
        get_sg();
        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            int nn,k=0;scanf("%d",&nn);
            for (int j=1;j<=nn;j++) scanf("%d",&a[i]),k^=sg[a[i]];
            if (!k) printf("L");else printf("W");
        }
        printf("\n");
    }
}

3、

POJ2425 A Chess Game

题解:

要用记忆化搜索求SG函数,注意SG函数是一个数字!一个状态的SG取决于ta的后继,后继所有SG的mex

代码:

#include <cstdio>
#include <cstring>
using namespace std;
int tot,nxt[499500],point[1005],v[499500],sg[1005];
void addline(int x,int y){++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}
int get_sg(int x)
{
    if (sg[x]!=-1) return sg[x];
    bool ext[1005];memset(ext,0,sizeof(ext));
    for (int i=point[x];i;i=nxt[i])
    {
        int t=get_sg(v[i]);
        ext[t]=1;
    } 
    for (int i=0;;i++)
      if (!ext[i]){sg[x]=i;break;}

    return sg[x];
}
int main()
{
    int x,k,m,n;
    while (~scanf("%d",&n))
    {
        memset(point,0,sizeof(point));tot=0;
        memset(sg,-1,sizeof(sg));
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&k);
            for (int j=1;j<=k;j++) 
              scanf("%d",&x),x++,addline(i,x);
        }
        while (scanf("%d",&m)&&m)
        {
            k=0;
            for (int i=1;i<=m;i++) 
            {
                scanf("%d",&x);x++;
                k^=get_sg(x);
            }
            if (!k) printf("LOSE\n");else printf("WIN\n");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值