继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

博弈——Nim博弈(hdu2176,1850,1851,1907,1849)

九日王朝
关注TA
已关注
手记 180
粉丝 42
获赞 185

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2176

http://acm.hdu.edu.cn/showproblem.php?pid=1850

http://acm.hdu.edu.cn/showproblem.php?pid=1851

http://acm.hdu.edu.cn/showproblem.php?pid=1907

*如对必胜态,必败态不是很理解的请移步上篇博客http://blog.csdn.net/sm9sun/article/details/53229146


题目描述:先抛一个取石子问题:

有n堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)

假设:

          如果n为1时,那么对于我方来说是必胜态,我可以把全部石子拿走。

          如果n为2时,那么我需要做的是逼迫对方拿光其中一堆,当然对方也不傻,所以我需要把石子变成1:1这样对方就必须拿光一堆。

          所以,当我面对两堆不一样数量的石子时,我的操作是使其两堆石子数相等,之后无论对方怎么拿,我都再另一堆也拿相同的石子数,逐步逼近1:1

          可见,这种情况我方为必胜态,反之,如果有两堆相同的石子,我方为必败态。

          如果n>2呢?比如说3,那么先举几个例子看一下,如果含有0,比如说(0,2,2)那么就回到了(2,2)的状态。

         如果是(1,2,3)不难发现,我方处于必败态,因为我无法一步将其变为(0,n,n),而对方在我做任何操作

        (0,2,3)(1,1,3)(1,2,2)(1,0,3)(1,2,1)(1,2,0)后,都可以变成(0,n,n)

         在这样的博弈中,我们不会轻易的拿掉一个独立的1,因为独立的1是完全颠覆胜败的操作

         所以无论m有多少,我们都可以将其视为最终的m个0和1,这个1的奇偶性决定着此次博弈的胜败

         所以(1,2,3 )我们可以将(2,3)当作是(0,1),我们不妨认为胜者必然会通过一轮双方都取2的操作来使其局势变为(1,0,1)

         联系到0和1,我们就不难想到计算机中的二进制了,所以我们用到的就是计算机中的按位异或运算。

         当我们面对一个(a,b,c)时,我们想办法使c变成a^b即可达到必胜态,因为a^b^(a^b)=(a^a)^(b^b) =0^0

         m堆石子同理

         举个例子:题中的(3,6,9)即0011,0110,1001。

         0011^0110^1001的结果是1100,所以我们要看以上3个数字能否拿掉若干石子达到改变 1100这个值

         0011  ^ 1100=1111  即3-15  不满足

         0110  ^ 1100=1010  即6-10  不满足

         1001  ^ 1100=0101  即9-5    可行

         也就是说,我把9拿掉4后变成5,局势变为(3,6,5)0011,0110,0101

         0011^0110^0101其结果为0000   满足对方的必败态。


          

#include<stdio.h>int main (){int a[200001],i,x,m;while(~scanf("%d",&m)&&m){  	x=0;for(i=1;i<=m;i++){scanf("%d",&a[i]);x^=a[i];}if(!x)printf("No\n");else{printf("Yes\n");for(i=1;i<=m;i++){if((x^a[i])<a[i])printf("%d %d\n",a[i],x^a[i]);}}}return 0;}



 题目1850、1851、1907同上




题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1849


题目描述:一个1*n的棋盘,编号为0~n-1,有m个棋子(可重叠)在棋盘上,每次可以将一枚棋子左移直至编号0的位置

最后一位走棋者胜。


讲道理题目中棋子左移操作很仁义了(好评),因为这很容易能让我们联系到 当棋子到第0位置时即无法再移动。

所以这道题我们可以转化为:  有m堆石子(棋子),每堆石子有不同的个数(棋盘编号),你可以拿走若干个石子(左移),最后一位走棋者(直至一堆)为胜。


所以这也是完完全全没有任何加料的Nim博弈~


#include<stdio.h>int main(){int i,b,m;int nim;while(~scanf("%d",&m)&&m){nim=0;for(i=0;i<m;i++){scanf("%d",&b);nim^=b;}if(nim)printf("Rabbit Win!\n");else printf("Grass Win!\n");}return 0;}



打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP