为什么写这篇博客!
我要控诉这个让我难受的世界!网上找到的尼姆博弈的基本上都是一句话:“也不知道是谁这么牛逼把这个东西和二进制联系在了一起”,要么就是大佬一句话带过地写了一下异或或者尼姆和即可!
哼!害我推导了半个多小时!
不过好在是看出来了,其实很简单。
关于尼姆博弈的题目相信大家也见过了,否则不会来看我这篇鬼扯的文章。这里还是简述一下题目:说现在有三堆石子,然后俩人轮流取,每次可以取其中的一堆,每次取至少取一个,至多可以直接把这堆拿走,能拿到最后一个石子的人算胜利。假设AB两人,A先手。【两人都非常聪明】——博弈论必备条件。
我们这里简化一下,假设只有两堆石子。那么,当两堆石子数量相等的时候,我们的A同志是必败的。(关于这点你要是不信可以找几个简单的情况自己在纸上画一画,两人都很聪明的条件下都不想输,那么A这个时候只有输的份儿)
很好!我们已经找到了一个非常非常牛逼的规律!当两堆石子数量相等的时候,先手必输!
那么我们再来想这么一个问题,假设存在有第三堆石子,前两堆石子的数量相同,最后一堆石子数量不同,比如1,1,3,我们看一下,此时A同志又可以必赢,这是为什么呢?我们可以发现,第三堆石子A可以直接拿走,转化成了B先手,两堆石子数量一样的情况。
那么如果是1,1,1的情况呢?你很容易发现A其实必赢。
好了,情况列举就到这里,接下来我们要开始进行推导了。
由【任意两堆数量相同的石子博弈情况为先手必输】再加上1,1,1情况先手必赢,我们开始进行总结。
我们在取第三堆石子的时候要考虑到前两堆石子的输赢情况,当然这里取石子的方法,顺序我们并不能决定,我们只是计算有没有必赢的情况。
现在!一个牛逼的家伙要把这个东西和二进制联系到一起了!
我们知道,石子的数量肯定是非负整数,那就肯定有其二进制表示!
比如两堆石子数量分别为7和5
二进制分别为:111和101。那么7就可以看作为4+2+1,5可以看作为4+1。对!二进制拆分!
111=100+10+1,101=100+1。
换成十进制就是:
7=4+2+1,5=4+1。
现在!石子的堆数变了!不是两堆!是五堆石子!其中两堆的数量为4,两堆为1,这两堆里先手可以必输,然后最后一堆数量不管多少直接拿走取得胜利!
至此就解释了为什么这个东西可以和二进制联系在一起。
那么再解释一下这个鬼东西为什么要和二进制联系在一起。因为我们看到上述变形里面我们知道了数量为4和1的那几堆使得先手在那里必输,那么我们可不可以在二进制运算里面把他们直接归零不计呢?
对!可以!用异或就很简单!而且在C语言里面可以直接写出来异或运算!方便快捷还省事儿!而且人家直接就是按照二进制去算的!这一步就直接把转换二进制什么的全给做完了。
按照上述例子,7和5,异或计算过后答案是2。答案是2!这个2何其熟悉!如果两个数字相同异或结果为0的话,先手必输。反之不为0就先手可以必赢。0为假非0为真!而且异或后的结果是什么呢?是去除了所有二进制拆分后数量相等的石子堆之后还剩余的石子。
下面我们再来扩展一下,n堆石子的情况。
假设n为3,三堆石子数量分别为a,b,c。如果我们a^b==0那么c只要不为0的话A就可必赢,因为前面A可以必输。后面A就可以躺赢。所以,如果a^b^c!=0,A就可以必赢。为什么呢?我们把ab合成一堆去看,会发现这时问题简化成为了两堆石子。两堆石子!也就说两堆石子只要数量不同就可以!
同理,n=1,2,3,4,5...都可以。只要a^b^c^d^.......!=0,那么A就可必赢!
emm...好吧其实这是河南省2018年的ACM省赛题目,当时是作为观摩队伍去的...和队友一起看出来规律但是没敢交(菜地一批...)官方给的标准程序也就是15行而已。
代码时间OvO:
#include
int main()
{
int n,a=0,b;//初始化a为0为了保证不管异或谁都可以
scanf("%d", &n);//数字个数
while (n--)
{
scanf("%d", &b);
a ^= b;//异或!!!
}
printf(a ? "YES\n" : "NO\n");//最后判断输出即可
return 0;
}
喜欢别忘了收藏点赞一条龙啊QAQ