[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律

【题目】题目链接

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.

InputIntput contains multiple test cases. The first line is an integer 1T1001≤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[n1]s[0],s[1],....,s[n−1], representing heaps with s[0],s[1],...,s[n1]s[0],s[1],...,s[n−1] objects respectively.(1n106,1s[i]109)(1≤n≤106,1≤s[i]≤109)OutputFor 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

【题意】
类似尼姆游戏(n堆石子,两人轮流从一堆中取出若干石子),加入了一个操作:可以 不取走石子而是把石子分为三堆(每堆不可为空)
恰好将所有石子取完的一方胜

【题解】
普通尼姆博弈 n堆石子的后继状态为0~n-1,所有堆的石子数异或和为0时为必败状态。
而拆分操作,例如将10拆为2 3 5,实际上相当于后继状态 2^3^5=4,把7拆为1 2 3相当于后继状态1^2^3=7
7的所有后继状态为 0 1 2 3 4 5 6 1^1^5 1^2^4 1^3^3 2^2^3
然后用SG函数的性质打表就可以了(SG(X)=后继状态没有出现的最小自然数)
观察可以出
SG(X)=X+1 (X%8==7)
SG(X)=X-1 (X%8==0)
SG(X)=X 其他

【SG定理】
P点:必败点,某玩家位于此点,只要对方无失误,则必败
N点:必胜点,某玩家位于此点,只要自己无失误,则必胜
定理:
1.所有总结点都是P点。
2.无论任何操作,必败点P只能进入必胜点N。
3.从任何必胜点N,至少有一种方式进入必败点P。

Mex运算:
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。
即:mex(S)=min{x},x∈N,x∉S
例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

SG函数:

 

 定理1:

1.有向图游戏的某个局面必胜,当且仅当该局面对应的节点的SG函数值大于0。

2.有向图游戏的某个局面必败,当且仅当该局面对应的节点的SG函数值等于0。

定理2:

多个有向图游戏组成的游戏{Gi,n}必胜,当且仅当有向图游戏的和SG函数值不为0.

【打表代码】

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int g[1010];
 4 void init() {
 5     memset(g, -1, sizeof(g));
 6 }
 7 int getSG(int x) {
 8     if(g[x] != -1) return g[x];
 9     if(x == 0) return 0;
10     if(x == 1) return 1;
11     if(x == 2) return 2;
12     int vis[110];
13     memset(vis, 0, sizeof(vis));
14     for(int i = 1; i < x; i++) {
15         int t = 0;
16         int a = getSG(i);
17         vis[a] = 1;
18         for(int j = 1; j < x - i; j++) {
19             int b = getSG(j);
20             int c = getSG(x - i - j);
21             vis[a^b^c] = 1;
22         }
23     }
24     vis[0] = 1;
25     for(int i = 0; ; i++) if(!vis[i])
26         return g[x] = i;
27 }
28 int main() {
29     int n;
30     init();
31     for(int i = 1; i <= 100; i++) {
32         g[i] = getSG(i);
33         printf("%d %d %d
", i, i % 8, g[i]);
34     }
35 }

【AC代码】

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int getsg(int x){
 4     if(x%8==0)return x-1;
 5     if(x%8==7)return x+1;
 6     return x;
 7 }
 8 int main(){
 9     int t,n,c,ans;
10     scanf("%d",&t);
11     while(t--){
12         ans=0;
13         scanf("%d",&n);
14         for(int i=1;i<=n;i++){
15             scanf("%d",&c);
16             ans^=getsg(c);
17         }
18         if(ans)printf("First player wins.
");
19         else printf("Second player wins.
");
20     }
21     return 0;
22 }
原文地址:https://www.cnblogs.com/conver/p/12200745.html