[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律
Posted conver
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律相关的知识,希望对你有一定的参考价值。
【题目】题目链接
InputIntput contains multiple test cases. The first line is an integer 1≤T≤1001≤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]s[0],s[1],....,s[n−1], representing heaps with s[0],s[1],...,s[n−1]s[0],s[1],...,s[n−1] objects respectively.(1≤n≤106,1≤s[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 }
以上是关于[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律的主要内容,如果未能解决你的问题,请参考以下文章
HDU 5795 A Simple Nim 打表求SG函数的规律
[hdu-5795]A Simple Nim 博弈 尼姆博弈 SG函数打表找规律
hdu5795 A Simple Nim 求nim求法,打表找sg值规律 给定n堆石子,每堆有若干石子,两个人轮流操作,每次操作可以选择任意一堆取走任意个石子(不可以为空) 或者选择一堆,把它分成