UVa 10561 (SG函数 递推) Treblecross

如果已经有三个相邻的X,则先手已经输了。

如果有两个相邻的X或者两个X相隔一个.,那么先手一定胜。

除去上面两种情况,每个X周围两个格子不能再放X了,因为放完之后,对手下一轮再放一个就输了。

最后当“禁区”布满整行,不能再放X了,那个人就输了。

 

每放一个X,禁区会把它所在的线段“分割”开来,这若干个片段就可以看做若干个游戏的和。

设g(x)表示x个连续格子对应的SG函数值,递推来求g(x):

g(x) = mex{ g(x-3), g(x-4), g(x-5), g(x-6) xor g(1), g(x-7) xor g(2)... }

g(0) = 0, g(1) = g(2) = g(3) = 1

枚举策略时,就是模拟下一步的状态,记录其中所有必败状态。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int maxn = 200;
 5 char s[maxn + 10];
 6 int g[maxn + 10], ans[maxn + 10];
 7 bool vis[maxn + 10];
 8 
 9 bool winning(const char* s)
10 {
11     int n = strlen(s);
12     for(int i = 0; i < n-2; i++)//已经有三个相邻的X,先手输
13         if(s[i] == 'X' && s[i+1] == 'X' && s[i+2] == 'X') return false;
14 
15     bool no[n+1];
16     memset(no, false, sizeof(no));
17     for(int i = 0; i < n; i++) if(s[i] == 'X')
18     {
19         for(int d = -2; d <= 2; d++)
20         {
21             if(i+d >= 0 && i+d < n)
22             {
23                 if(d != 0 && s[i+d] == 'X') return true;//有两个X在彼此的禁区,先手胜
24                 no[i+d] = true;//设置禁区
25             }
26         }
27     }
28 
29     no[n] = 1;
30     int sg = 0;
31     for(int i = 0; i < n; i++)
32     {
33         if(no[i]) continue;
34         int cnt = 0;
35         while(i < n && !no[i]) { i++; cnt++; }
36         sg ^= g[cnt];
37     }
38     return sg != 0;
39 }
40 
41 int main()
42 {
43     //freopen("in.txt", "r", stdin);
44 
45     g[0] = 0;
46     g[1] = g[2] = g[3] = 1;
47     for(int i = 4; i <= maxn; i++)
48     {//递推求函数g
49         memset(vis, false, sizeof(vis));
50         for(int j = 3; i-j >= 0; j++)
51         {
52             int v = 0;
53             v ^= g[i-j];
54             int x = j - 5;
55             if(x > 0) v ^= g[x];
56             vis[v] = true;
57 
58             for(int j = 0; ; j++) if(!vis[j]) { g[i] = j; break; }
59         }
60     }
61 
62     int T;
63     scanf("%d", &T);
64     while(T--)
65     {
66         scanf("%s", s);
67         if(!winning(s)) { printf("LOSING\n\n"); continue; }
68 
69         puts("WINNING");
70         int n = strlen(s);
71         memset(ans, 0, sizeof(ans));
72         int p = 0;
73         for(int i = 0; i < n; i++) if(s[i] == '.')
74         {
75             s[i] = 'X';
76             if(!winning(s)) ans[p++] = i+1;//后继必败状态便是先手下一步的策略
77             s[i] = '.';
78         }
79         for(int i = 0; i < p; i++)
80         {
81             if(i) printf(" ");
82             printf("%d", ans[i]);
83         }
84         printf("\n");
85     }
86 
87     return 0;
88 }
代码君

 

posted @ 2015-03-10 15:06  AOQNRMGYXLMV  阅读(276)  评论(0编辑  收藏  举报