HDU 1850 Being a Good Boy in Spring Festival(Nim博弈)

打开题目网址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;
}


 

原文地址:https://www.cnblogs.com/zswbky/p/5432053.html