hdu 1850 Being a Good Boy in Spring Festival(尼姆博弈)

题目链接:hdu 1850(尼姆博弈)

Being a Good Boy in Spring Festival

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description
一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~

下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i = 1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”

Input
输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1 < M <= 100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1 <= Ni <= 1000000,i = 1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。

Output
如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。

Sample Input
3
5 7 9
0

Sample Output
1

此题中ans = g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i]] ^ … ^ g[arr[M - 1]],

ans == 0时先手必败,因此只能输出0,
ans != 0时,ans ^ g[arr[i]] == g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i - 1]] ^ g[arr[i + 1]] ^ … ^ g[arr[M - 1]],

只需使原ans中的g[a[i]] = ans ^ g[a[i]],就可以使
ans = g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i - 1]] ^ g[arr[i + 1]] ^ … ^ g[arr[M - 1]] ^ (g[arr[0]] ^ g[arr[1]] ^ g[arr[2]] ^ …^ g[arr[i - 1]] ^ g[arr[i + 1]] ^ … ^ g[arr[M - 1]])
= (g[arr[0]] ^ g[arr[0]]) ^ (g[arr[1]] ^ g[arr[1]]) ^ (g[arr[2]] ^ g[arr[2]]) ^…^ (g[arr[i - 1]] ^ g[arr[i - 1]]) ^ (g[arr[i + 1]] ^ g[arr[i + 1]]) ^ … ^ (g[arr[M - 1]] ^ g[arr[M - 1]])
= 0,

此时后手处于奇异局势(后手必败),说明这个策略是可选的。
注意ans ^ g[arr[i]]必须保证小于arr[i],若前者大于后者,在这道题中,g[arr[i]] == arr[i],若是改变反而相当于要加牌进去了;若前者与后者相等,就相当于没有做任何操作。这两种情况明显都是不合法的。

PS:
我在一些这道题的题解里看到与这段话意思相近的话:

对于某个局面(a1,a2,…,an),若a1^a2^…^an!=0,一定存在某个合法的移动,将ai改变成ai’后满足a1^a2^…^ai’^…^an=0。不妨设a1^a2^…^an=k,则一定存在某个ai,它的二进制表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k < ai一定成立。则我们可以将ai改变成ai’=ai^k,此时a1^a2^…^ai’^…^an=a1^a2^…^an^k=0.

起初我一直没有看懂Orz,直到看到了这个:
【转】博弈论(一)

上面那段话是这篇文章里的一段原话(虽不知是否最初出自这里),这一段的原意是要证明以下命题的第二个:

证明一种判断position的性质的方法的正确性,只需证明三个命题:
1、这个判断将所有terminal position判为P-position;
2、根据这个判断被判为N-position的局面一定可以移动到某个P-position;
3、根据这个判断被判为P-position的局面无法移动到某个P-position。

第二个命题稍微转换一下就是在问:
能够使一个非奇异局势转变为一个奇异局势的移动操作的存在性

上面引用的证明过程中ai ^ k < ai这个条件是k最高位的1来自于ai时才成立的,后者是前者的充分条件,没有给出必要性的证明。

条件为k最高位的1来自于ai,那么ai ^ k进行运算时,有两种情况:
1.ai的最高位1就是k的最高位1,由于2 ^ n > 2 ^ (n - 1) + 2 ^ (n - 2) + …… + 2 + 1,毫无疑问ai ^ k < ai;
2.ai的最高位1高于k的最高位1,此时ai的最高位1到k的最高位1之间不会发生变化,ai的提供k最高位1的1会变成0,但由于同上的原因,无论之后的n - 1位发生什么样的变化,结果都会是ai ^ k < ai.

因此得出结论:当且仅当ai ^ k < ai时,k最高位的1来自于ai,取ai’ = ai ^ k替换原式中的ai时,能够使一个非奇异局势转变为一个奇异局势。

这个证明是自己想的,数学证明一直是短板,难免有疏漏,还请各位反馈,我会及时订正(当然前提是有人看的话Orz)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define MAX 1000005
#define MM 105
#define N 5

using namespace std;

int arr[MM];
int g[MAX];
int cnt;

void SG()//这个sg函数不写也行,只是个形式
{
    for(int i = 0; i < MAX; i++)    g[i] = i;
}

int solve(int ans, int b)//同上
{
    return ans ^ g[b];
}

int main()
{
    SG();
    int M;
    while(scanf("%d", &M) && M)
    {
        cnt = 0;
        scanf("%d %d", &arr[0], &arr[1]);
        int ans = g[arr[0]] ^ g[arr[1]];
        for(int i = 2; i < M; i++)
        {
            scanf("%d", &arr[i]);
            ans = solve(ans, arr[i]);
        }
        if(ans)
        {
            for(int i = 0; i < M; i++)
            {
                if((ans ^ arr[i]) < arr[i])//注意优先级
                    cnt++;
            }
            printf("%d\n", cnt);
        }
        else
            printf("0\n");
    }
    return 0;
}

运行结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值