UVA1378 A Funny Stone Game —— SG博弈

题目链接:https://vjudge.net/problem/UVA-1378

题意:

两个人玩游戏,有n堆石子,两人轮流操作:于第i堆石子中取走一块石子,然后再往第j、k堆中各添加一块石子。其中 i<j, j<=k。最后一次操作的为赢家,问先手能否必胜,如果能,请输出第一步操作。

题解:

1.把每个石子都看成是一个子游戏,所以在第i堆的其中一个石子,它的下一步为第j堆和第k堆;即这个子游戏又可以分解为两个子游戏。

2.由于在同一堆里的石子状态完全相同,所以当这堆石子的个数为偶数时,他们的SG值异或和为0;当个数为奇数时,他们的SG值异或和即为单独一个石子的SG值。

3.那么怎么求在第i堆里一个石子的SG值呢?一般情况下子游戏的下标是较小的,而处于第i堆的石子游戏又可以分解为处于第j、k石子的子游戏,然后i是小于j、k的,所以我们就把下标翻过来,这样就满足的SG的求值过程。初始状态为SG[0] = 0,即位于第0堆时,无法操作,即无后续状态,所以 SG[0] = mex{} = 0。

4.至于输出第一步,枚举ijk,得到一个必败状态即可。为何是必败状态?因为先手是处于必胜状态,当先手操作了一轮之后,轮到后手,而后手就处于必败状态。

代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <cmath>
 7 #include <queue>
 8 #include <stack>
 9 #include <map>
10 #include <string>
11 #include <set>
12 using namespace std;
13 typedef long long LL;
14 const int INF = 2e9;
15 const LL LNF = 9e18;
16 const int MOD = 1e9+7;
17 const int MAXN = 30+10;
18 
19 int a[MAXN], SG[MAXN], vis[1000];
20 
21 void getSG()
22 {
23     SG[0] = 0;
24     for(int i = 1; i<MAXN; i++)
25     {
26         memset(vis, 0, sizeof(vis));
27         for(int j = 0; j<i; j++)
28             for(int k = 0; k<=j; k++)
29                 vis[SG[j]^SG[k]] = 1;   //下一个状态,由两个子游戏组成,故异或。
30 
31         for(int j = 0;;j++) if(!vis[j]) {
32             SG[i] = j;
33             break;
34         }
35     }
36 }
37 
38 int main()
39 {
40     getSG();
41     int n, kase = 0;
42     loop:
43     while(scanf("%d",&n) && n)
44     {
45         int val = 0;
46         for(int i = 0; i<n; i++)
47         {
48             scanf("%d", &a[i]);
49             if(a[i]%2) val ^= SG[n-1-i];    //当个数为奇数时,才有效
50         }
51 
52         printf("Game %d: ", ++kase);
53         for(int i = 0; i<n-1; i++)  //枚举、模拟第一步操作
54         {
55             if(a[i]==0) continue;   //如果这堆没有石子,那就不能取了。
56             for(int j = i+1; j<n; j++)
57             for(int k = j; k<n; k++)   //取走i堆一块石子,往j、k堆中各加一个石子,即撤销i堆中一个子游戏
58                 if((val^SG[n-i-1]^SG[n-1-j]^SG[n-1-k])==0){ //j、k堆中增加一个游戏。异或的作用是:有则去之无则添之。
59                     printf("%d %d %d
", i, j, k);
60                     goto loop;
61                 }
62         }
63         printf("-1 -1 -1
");
64     }
65     return 0;
66 }
View Code
原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8351413.html