#1173 : 博弈游戏·Nim游戏·三
-
3 1 2 4
样例输出
-
Bob
描述
在这一次游戏中Alice和Bob决定在原来的Nim游戏上增加一条规则:每一次行动时,不仅可以选择一堆取走任意数量的石子(至少取1颗,至多取出这一堆剩下的所有石子),还可以选择将一堆石子分成两堆石子,但并不取走石子。比如说有一堆石子为k个,当Alice或者Bob行动时,可以将这一堆石子分成两堆,分别为x,y。满足x+y=k,x,y>0。那么增加了这一条规则后,在Alice总先手的情况下,请你根据石子堆的情况判断是Alice会获胜还是Bob会获胜?
输入
第1行:1个整数N。表示石子堆数。1≤N≤100
第2行:N个整数,第i个整数表示第i堆石子的个数A[i],1≤A[i]≤20000
输出
第1行:1个字符串,若Alice能够获胜输出"Alice",否则输出"Bob"
这道题需要用到sg函数,对于每堆石子,假设大小为x,它的后继有1,2,3...x-1已经(1,x-1),(2,x-2)..根据sg定理(1,x-1)的sg值就是sg(1)^sg(x-1),所以x的后继的sg值就是(这题由于有分堆的操作,所以sg(x)就不一定等于x了)sg(1)、sg(2)、sg(3)...以及sg(1)^sg(x-1)、sg(2)^sg(x-2)...的集合,这样我们就看求出每个x的sg值了。
但是x的上限是20000,显然不能完全打表求sg值,可以通过找sg值的规律解题。
先打出1到100的sg值:
1 2 4 3 5 6 8 7 9 10
12 11 13 14 16 15 17 18 20 19
21 22 24 23 25 26 28 27 29 30
32 31 33 34 36 35 37 38 40 39
41 42 44 43 45 46 48 47 49 50
52 51 53 54 56 55 57 58 60 59
61 62 64 63 65 66 68 67 69 70
72 71 73 74 76 75 77 78 80 79
81 82 84 83 85 86 88 87 89 90
92 91 93 94 96 95 97 98 100 99
我们很容易发现奇数对的数都是本身,比如1,2和5,6,而偶数位的数都发生了交换,比如sg(3)=4,sg(4)=3,这样我们就找到规律了,可以根据规律得到子游戏的sg值,最后求一下异或和就可以做出
代码:
#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 20000;
int sg[maxn+5];
int book[maxn+6];
void init()
{
int i, j;
for(i=1; i<=100; i++)
{
for(j=1; j<=100; j++)book[j]=0;
for(j=1; j<i; j++)
{
book[sg[j]]=1;
book[sg[j]^sg[i-j]]=1;
}
for(j=1; j<=1000; j++)if(book[j]==0)break;
sg[i]=j;
}
j=0;
for(i=1; i<=100; i++)
{
printf("%d ", sg[i]);
j++;
if(j%10==0)printf("\n");
}
}
int main()
{
// init();
int i, j;
int n, x, sg=0;
cin>>n;
for(i=0; i<n; i++)
{
scanf("%d", &x);
if(((x+(x&1))>>1)&1)sg^=x;
else sg^=(x&1)?x+1:x-1;
}
if(sg)printf("Alice\n");
else printf("Bob\n");
}