SDUT 3566 第七届山东省ACM省赛 Triple Nim (Nim博弈 + 二进制)

传送门:SDUT 3566



题目大意:

让你把 n 个石子分为 3 堆并进行 Nim 博弈,问使得先手必败的方案数有多少?



思路:

对于有 n 堆石子的 Nim 博弈,要先手必败则 n 堆石子个数异或值为 0. 也就是说 n 堆石子个数的二进制表示中每一位的 1 的个数之和必定是偶数。


1. 当 n 为奇数时,一定会分成 2 个偶数堆和一个奇数堆,异或值肯定不为 0。


2. 当 n 为偶数时,二进制中高位的 1 必定要变为次低位中的 2 个 1,否则高位中的 1 的个数和为奇数,不能使得异或值为 0.


如: n=14时,二进制为 (1110),把高位的 1 变为次低位的 2 个 1,则三堆石子数的一种情况为:

二进制位数:    0  1  2  3

第一堆:           0  0  0  0

第二堆:           0  1  1  1

第三堆:           0  1  1  1

其中第 1、2、3 位中有 1 个 0,这个 0 可以分配给任意的一堆,但是不可以同时分配给同一堆,并且因为求得是排列数,要除以6,结果为 (3^3 - 3)/6 = 4 .


拓展到一般情况,分成的 3 堆石子的二进制某一位中 1 的个数和为 2 (或者说 0 的个数为 1)的个数有 num 个,其中 num 为 n 的二进制表示中 1 的个数。而这 num 个位置的 0 可以分配给 3 堆石子中的任意一个,所以有 3^num 种情况。


而这中间可能会把每个位的 0 都分配给一堆石子,导致某堆石子数为 0,所以要减去 3,又由于上面我们计算的是 3 个数的排列,有重复,所以要将上面的结果除以 6,最终结果为 (3^num - 3) / 6 .



代码:

#include<stdio.h>
#include<math.h>
typedef long long LL;
int main()
{
	int i,t,n,num,ans;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		if(n%2) printf("0\n"); //奇数 
		else
		{ //偶数 
			num=0;
			while(n)
			{ //二进制中1的个数 
				if(n&1) num++;
				n>>=1;
			}
			ans=((LL)pow(3,num)-3)/6; //注意要加(LL)强制转换 
			printf("%lld\n",ans);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zuzhiang/article/details/80115842
Nim