[博弈论]hiho#1173 : 博弈游戏·Nim游戏·三

首先给出基本定义:

对于一个游戏可能发生的局面x,我们如下定义它的sg值:
(1)若当前局面x为终结局面,则sg值为0。
(2)若当前局面x非终结局面,其sg值为:sg(x) = mex{sg(y) | y是x的后继局面}。
mex{a[i]}表示a中未出现的最小非负整数。举个例子来说:
mex{0, 1, 2} = 3, mex{1, 2}=0, mex{0,1,3}=2

sg定理:
对于多个单一游戏,X=x[1..n],每一次我们只能改变其中一个单一游戏的局面。则其总局面的sg值等于这些单一游戏的sg值异或和。
即:
sg(X) = sg(x[1]) xor sg(x[2]) xor … xor sg(x[n])
要证明这一点我们只要证明:
(1) 假设sg(x[1]) xor sg(x[2]) xor … xor sg(x[n]) = A,对于任意一个0 <= B < A,总存在一个X的后续局面Y,使得sg(Y) = B。
(2) 假设sg(x[1]) xor sg(x[2]) xor … xor sg(x[n]) = A,不存在一个X的后续局面Y,使得sg(Y) = A。

这是hiho里给出的解释,首先给出了定理,然后解释了定理并证明了,然后结合问题分析了问题应该怎么做。标准的中国式例题讲解思维。确实这样的逻辑会比较符合大多数人的思维。但是这里面是否又有些值得深思的呢?

代码就是依据定理来写的,求出所有的sg[].
另外,对于这个题每个sg值就有两种可能:
(1)不分堆:石子数量为k’=0..k-1,则sg(k’)
(2)分堆:石子变为2堆,数量为(1,k-1),(2,k-2),…,(k-1,1)。设第一堆的石子数量为i,则sg值为sg(i) xor sg(k-i)。(这里用到了sg定理)
那么可以推算出sg(k) = mex{sg(0), sg(i), sg(i) xor sg(k - i) | i = 1..k-1}。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
#define read freopen("q.in","r",stdin)
#define LL __int64
#define maxn 20004
#define inf 10000000
#define mod 142857
using namespace std;
int sg[maxn],a[maxn];
bool vis[maxn];
void Accepted()
{
    int i,j;
    sg[0]=0;

    for(i=1;i<=maxn;i++)
    {
        memset(vis,0,sizeof(vis));
        for(j=0;j<i;j++)
        {
            int t=sg[j]^sg[i-j];
            vis[sg[j]]=1;
            vis[t]=1;
        }
        for(j=0;j<maxn;j++)if(!vis[j])
        {
            sg[i]=j;
            break;
        }

    }
}
int main()
{
   int n,i,j,x;
   Accepted();
   scanf("%d",&n);
   int res;
   scanf("%d",&x);
   res=sg[x];
   for(i=1;i<n;i++)
   {
       scanf("%d",&x);
       res^=sg[x];
   }
   if(!res)cout<<"Bob"<<endl;
   else cout<<"Alice"<<endl;

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值