• 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
    
    "); 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("
    ");
    85     }
    86 
    87     return 0;
    88 }
    代码君
  • 相关阅读:
    可遇不可求的Question之无法加载 DLL
    可遇不可求的Question之mysql odbc 5.1 driver 指定驱动程序无法加载
    可遇不可求的Question之Got error 127 from table handler
    可遇不可求的Question之Odbc与MYSQLCLIENT转码机制探讨
    【转】Task Scheduler Managed Wrapper: 由程式來排程工作
    可遇不可求的Question之update from 语法
    【转】Long Time Operations in ASP.NET
    可遇不可求的Question之System.TypeLoadException错误
    浅谈Asp.Net中涉及到的四个TimeOut属性
    4月2号
  • 原文地址:https://www.cnblogs.com/AOQNRMGYXLMV/p/4326058.html
Copyright © 2020-2023  润新知