UVALive 3668 A Funny Stone Game(博弈)

Description 

The funny stone game is coming. There are n piles of stones, numbered with 0, 1, 2,..., n - 1. Two persons pick stones in turn. In every turn, each person selects three piles of stones numbered ijk (i < jjk and at least one stone left in pile i). Then, the person gets one stone out of pile i, and put one stone into pile j and pile k respectively. (Note: if j = k, it will be the same as putting two stones into pile j). One will fail if he can't pick stones according to the rule.

David is the player who first picks stones and he hopes to win the game. Can you write a program to help him?

The number of piles, n, does not exceed 23. The number of stones in each pile does not exceed 1000. Suppose the opponent player is very smart and he will follow the optimized strategy to pick stones.

Input

Input contains several cases. Each case has two lines. The first line contains a positive integer  n (  n 23) indicating the number of piles of stones. The second line contains  n non-negative integers separated by blanks,  S0,...Sn-1 (  Si 1000), indicating the number of stones in pile  0 to pile  n - 1respectively.

The last case is followed by a line containing a zero.

 

Output

For each case, output a line in the format ``  Game tijk".  t is the case number.  ij and  k indicates which three piles David shall select at the first step if he wants to win. If there are multiple groups of  ij and  k, output the group with the minimized lexicographic order. If there are no strategies to win the game,  ijand  k are equal to  -1.
 
题目大意:有n堆石头,第i堆石头有Si个石头,每次可以从第i堆石头拿掉一个石头,然后在第j、k(i<j、k)上各放一个石头。两人轮流操作,不能操作的人算输,问先手第一步的必胜策略是什么,输出最小字典序的答案。
思路:利用SG函数来做。可以把每个石头看作是独立的游戏,那么对于某个在第i位的石头,它的后续状态是所有的在第j、k位的两个石头,这两个石头也是独立的游戏,那么sg[i] = mex{sg[j] ^ sg[k]},对于每个位置的sg[i]可以O(n^3)预先计算出来。
然后所有的石头的SG值异或若等于0则输出-1-1-1(对于每一个Si,若Si为奇数,则异或一次,若Si为偶数,则不异或,因为一个数异或偶数次,肯定是0,复杂度为O(n)),否则O(n^3)暴力枚举方案,若sg[i]^sg[j]^sg[k]^ans==0,则i、j、k是必胜方案,其中ans是所有石头SG值异或的结果。
就是一步操作i、j、k,sg[i]会变成sg[j]^sg[k],那么若原来的SG值为ans,又sg[i]^sg[j]^sg[k]^ans==0,那么新状态的SG值就为0(可以参考NIM博弈的证明)。
 
代码(3MS):
 1 #include <cstdio>

 2 #include <algorithm>

 3 #include <iostream>

 4 #include <cstring>

 5 using namespace std;

 6 

 7 const int MAXN = 23;

 8 

 9 int sg[MAXN + 5];

10 bool mex[MAXN * MAXN];

11 int n, s[MAXN];

12 

13 void build_sg() {

14     for(int i = MAXN - 1; i >= 0; --i) {

15         memset(mex, 0, sizeof(mex));

16         for(int j = i + 1; j < MAXN; ++j) {

17             for(int k = j; k < MAXN; ++k) mex[sg[j] ^ sg[k]] = true;

18         }

19         for(int j = 0; ; ++j)

20             if(!mex[j]) {sg[i] = j; break;}

21     }

22 }

23 

24 void solve() {

25     int ans = 0, *sg = ::sg + MAXN - n;

26     for(int i = 0; i < n; ++i) ans ^= sg[i] * (s[i] & 1);

27     if(ans == 0) {

28         printf(" -1 -1 -1\n");

29         return ;

30     }

31     for(int i = 0; i < n; ++i) {

32         if(s[i] == 0) continue;

33         for(int j = i + 1; j < n; ++j) {

34             for(int k = j; k < n; ++k) {

35                 if((sg[i] ^ sg[j] ^ sg[k] ^ ans) == 0) {

36                     printf(" %d %d %d\n", i, j, k);

37                     return ;

38                 }

39             }

40         }

41     }

42 }

43 

44 int main() {

45     build_sg();

46     int cnt = 0;

47     while(scanf("%d", &n) != EOF && n) {

48         for(int i = 0; i < n; ++i) scanf("%d", &s[i]);

49         printf("Game %d:", ++cnt);

50         solve();

51     }

52 }
View Code

 

你可能感兴趣的:(game)