(HDU - 3032)Nim or not Nim?(尼姆博弈变形)

147 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

题目链接:Nim or not Nim? - HDU 3032 - Virtual Judge (ppsucxtt.cn)

题目大意:给你n堆石子,两个人轮流进行操作,每次可以从任意一堆中取走任意数量的石子,也可以将一堆石子分成两堆,最后无法操作者输。

这一看就是一个尼姆博弈的变形题目,尝试过后发现这道题目无法利用尼姆博弈本身的一些结论,我们只能利用sg函数进行求解,在求解sg函数时与尼姆博弈中求解sg函数过程差不多,就是多了一个分解操作,对于有n个石子的一堆石子,如果是一般的尼姆博弈的话,他的后继状态可能有1~n-1个石子的一堆,而对于本题而言,他的后继状态多了一些,对于任意的x+y=n,(x!=0,y!=0),都有可能成为他的后继,所以我们要考虑上这些情况的sg值,考虑上这些情况再求sg就可以了,但一看数据范围是1e6,所以显然是有具体的结论的,我们先将n小于等于100的情况打印出来,发现具有如下规律:

image.png

因此我们可以直接利用此规律求出每堆石子的sg值并进行异或即可,下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
//const int N=1e6+10;
//bool vis[N+10];
//int sg[N],f[N+10];
//void init()
//{
//	memset(sg,0,sizeof sg);
//	for(int i=1;i<=N;i++)
//	{
//		memset(vis,false,sizeof vis);
//		for(int j=1;f[j]<=i;j++)
//			vis[sg[i-f[j]]]=1;
//		for(int j=1;j<i;j++)
//			vis[sg[j]^sg[i-j]]=1;
//		for(int j=0;j<=N;j++)
//		{
//			if(!vis[j])
//			{
//				sg[i]=j;
//				break;
//			}
//		}
//	}
//}
int sg(int n)
{
	if(n%4==3) return n+1;
	else if(n%4==0) return n-1;
	else return n;
}
int main()
{
//	for(int i=1;i<=N;i++)	f[i]=i;
//	init();
//	for(int i=0;i<=N;i++)
//		printf("%d\n",sg[i]);
	int T,n,t;
	cin>>T;
	for(int i=1;i<=T;i++)
	{
		scanf("%d",&n);
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&t);
			ans^=sg(t);
		}
		if(ans) puts("Alice");
		else puts("Bob");
	}
	return 0;
}