[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律
【题目】题目链接
InputIntput contains multiple test cases. The first line is an integer
Sample Input Sample Output
定理1: 1.有向图游戏的某个局面必胜,当且仅当该局面对应的节点的SG函数值大于0。 2.有向图游戏的某个局面必败,当且仅当该局面对应的节点的SG函数值等于0。 定理2: 多个有向图游戏组成的游戏{Gi,n}必胜,当且仅当有向图游戏的和SG函数值不为0. 【打表代码】 【AC代码】2
2
4 4
3
1 2 4
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 #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 }
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 }