打开题目网址http://acm.hdu.edu.cn/showproblem.php?pid=1850
其实题目的意思很简单!就是一个Nim博弈!但是这道题里说的不是问谁获胜,而是问的获胜的方法有多少种!
所以这样说来好像还挺有意思的。。。
首先,我们必须知道,因为是起手,对于M堆扑克,最多有M种获胜的方法!(为什么???还不清楚...)
下面找了网有的一点解释。。。
对res = M1 ^ M2 ^ M3 …. ^ Mm取异或(^)得res,由异或的性质知:res ^ Mi = ( M1 ^ M2 …^M(i-1) ^ M(i+1) ^ … ^Mm ) ,也就是说res对任意一个数取^可以的得到其他所有数的^值,
例如:5 7 9;5^7^9=0101^0111^1001=1011=11;那么11^5=1011^0101=1110=0111^1001=14;
那么要保证先手取后剩余局势处于必败点,那么只有保证在这5张牌的牌堆里取出一些牌后,可以使得x^1110=0即x=1110=14,另外这个牌堆数必须大于14才有可能达到这种状态;
现在只有5张牌,显然不能,而对于牌堆9,有(res^9)=0010=2,9显然大于2,所以只需要拿走7张牌就可以让局势处于必败点!
终于弄懂了,看起来代码简短,好像蕴含了好多内容其实想想也没有多少内容,其实就是在最基本的Nim博弈中增加了方案的规划
想想看,在最后求出sum,也就是最后的异或结果之后,如果在和某一堆中的值异或的话
其实就相当于是把这一堆从原来的所有堆中去掉了,这样的话就可以将所有的情况分开来看了
这样sum^a[i]也就是没有加当前这一堆的sum,要想让对手成为必败态,就要在自己处理之后让sum变成零
这样相异或的结果(sum^a[i])就应该是在当前堆中要去掉的,但是必须要保证的是当前的堆中有sum^a[i]
个石子。。。
*/
#include<iostream> using namespace std; const int n=105; int main() { int i,temp,m,a[n],sum,count; while(~scanf("%d",&m) && m!=0) { sum=0; count=0; for(i=0;i<m;i++) { scanf("%d",&a[i]); sum^=a[i];//先把所有的值进行异或运算 } if(sum==0) cout<<0<<endl;//若nim-sum为0,则是必败点,否则是必胜点 else { for(i=0;i<m;i++) { temp=sum; temp=temp^a[i];//这其实是nim-sum定理,sum与每个值抑或 if(temp < a[i])//然后该堆就减少为异或后的值,这是由必胜点到必败点 count++;//但是注意该值肯定是小于之前的初始值 } printf("%d ",count); } } return 0; }