尼姆博弈

问题来源POJ 2234 Matches Game

问题描述

有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜
给定 n ,以及n怼物品各自的数量,求先手是否能赢得比赛

题目分析

在博弈游戏中存在两种状态:必胜状态和必败状态
一个状态是必败状态当且仅当它的所有后继都是必胜状态
一个状态是必胜状态当且仅当它至少有一个后继是必败状态
定理:将每个堆的数量逐个进行异或,如果值为0则先手输,否则,先手赢

证明:我们要证明这个定理,我们就可以在推理过程中,无论对方怎么取物品我们都运用这个定理,如果结果与定理结果相同,则证明了这个定理,那怎么运用? 因为根据定理异或值为0为必败,如果我们参与比赛,一定会让对手面对这个必败局面(异或值为0),对手无论怎样取,都会使得的异或值不为0,那么我们就有了一种策略:取走一定数量的物品,使其达到异或值为0的局面

我们知道,选手最后输是因为他面对(0,0……)n个0堆
那么现在假设有n堆物品,每个堆有a1,a2,a3,……,an件物品,那么如果异或结果a1^a2 ^a3 ^a4 ^……… ^an =0,则说明每堆物品数量的在其每个二进制数位上的1的数量为偶数(因为异或运算中,每一二进制位相同为0不同为1)
我们以(2,5,7)为例,2^ 5 ^7=0
1 0
^ 1 0 1
^ 1 1 1
------------
0 0 0
在最低位上有2个1,第二位上有2个1第三位上有2个1,我们将这样的状态姑且称为平衡状态
如果选手从任意一个堆上取走任意数量的物品,则势必会打破这个平衡(在某一位或者某几位上取走一个1,则一定会造成在这几个位上甚至其他位上的1的总的数量处于奇数状态)这个状态我们称为非平衡状态,其异或值为K
我们接着取物品,我们想是否可以取走一定数量的物品使得其再变为平衡状态,答案是可以的,试想既然现在的状态异或值为K,则一定存在某一堆的物品数量ai在K最大二进制位的位置为1,现在我们从这堆上取走(ai-ai^k)数量的物品
ai^k<ai(K最高位的位置变为了0)
设ai’=ai^ k,则a1^ a2^ …^ ai’^ …^ aN=a1^ a2^ ………^ ai^ ……^ aN^ k(代入ai’=ai^k) =k ^k=0
显然又回到了平衡态,这样循环往复,无论先手怎样取都会打破平衡状态,而后手一定有方法将非平衡状态变为平衡状态(非平衡状态下,至少有一堆的物品数量不为0)随着数量的不断减少,最后先手面对平衡状态(0,0……),先手败 ,与定理的结果相同,则证明了定理

代码实现

#include<iostream>
#include<cstdio>
using namespace std;
int main()
{
    int n;
    int a,b;
    while((scanf("%d",&n))!=EOF)
    {
        a=0;
        for(int i=0;i<n;i++)
        {
             cin>>b;
             a=a^b;
        }
        if(a==0)
            cout<<"No"<<endl;
         else
            cout<<"Yes"<<endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值