Problem Description
Two players take turns picking candies from n heaps,the player who picks the last one will win the game.On each turn they can pick any number of candies which come from the same heap(picking no candy is not allowed).To make the game more interesting,players can separate one heap into three smaller heaps(no empty heaps)instead of the picking operation.Please find out which player will win the game if each of them never make mistakes.
Input
Intput contains multiple test cases. The first line is an integer
1≤T≤100, the number of test cases. Each case begins with an integer n, indicating the number of the heaps, the next line contains N integers
s[0],s[1],....,s[n−1], representing heaps with
s[0],s[1],...,s[n−1] objects respectively.
(1≤n≤106,1≤s[i]≤109)
Output
For each test case,output a line whick contains either"First player wins."or"Second player wins".
Sample Input
2 2 4 4 3 1 2 4
Sample Output
Second player wins. First player wins.
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
using namespace std;
/*
打表部分
*/
int sg[32];
bool vis[32];
void print(){
for(int i=1;i<32;i++){
memset(vis,0,32);
int j,k;
for( j=0;j<i;j++) vis[sg[j]]=1;//find the >=0's intergers
for( j=1;j<i;j++)//save after divide;
for( k=1;j+k<i;k++)
vis[sg[j]^sg[k]^sg[i-k-j]]=1;
for (j=0; j<32; j++){ //find the min j doesn't belong to this sg;
if(!vis[j]) break;
}
sg[i]=j;
}
for(int i=1;i<32;i++){
cout<<i<<" "<<sg[i]<<endl;
}
}
/*
主程序部分
*/
int findsg(int s){
if(s%8==0) return s-1;
else if((s+1)%8==0) return s+1;
else return s;
}
int main(){
print();
int t,n;
scanf("%d",&t);
while(t--){
int ans=0;
int s;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&s);
ans^=findsg(s);
}
if(ans) printf("First player wins.\n");
else printf("Second player wins.\n");
}
}
题意:两个人在n堆里取糖果,每个人可以选择在同一堆里取任意数量的糖果或者把某一堆糖果分成三堆,求最后的胜者;
对于博弈游戏,我们首先要了解SG函数;
sg
(1)
(2)
(3)
(4)
(5)
我们可以通过计算sg值来定胜负,如果sg>0,则先手胜,反之则先手负;
如何计算sg值:
sg值可以理解为一个数能被继续操作的次数(通常sg(0)=0,sg(1)=1));一个数的sg值就是就是不等于它的后继点的sg值的最小非负数,几个堆的sg值就是每个堆sg值的异或;
譬如有三个堆,它们的后继点的sg值分别为0 1 2、0 1 3、0 1 4,那么sg值就为3^2^2=3,先手赢(此时不考虑分堆);
现在选手还可以选择分堆操作,那我们就同时要把分堆的情况考虑;
先用vis数组的下标将所有的后继点的sg值存下来,之后在存储所有可分状态分三堆的情况的sg值;
最后遍历vis数组,记录最小的、不属于已计算sg值的最小非负整数,则为当前数字的sg值;
这样可以根据需要打出sg值的表格,找到规律:每一个能被8整除的数,sg值为该数-1;它的前一个数为那个数+1(即前后交换sg值);其他数为本身;
将每个堆的sg值异或就是最后的结果;