• 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
  • 相关阅读:
    写在开始前表单数据校验
    Redis之时间轮机制(五)
    Reids高性能原理(六)
    redis持久化之RDB (七)
    Redis的使用(二)
    redisson之分布式锁实现原理(三)
    redis主从复制(九)
    Redis的内存淘汰策略(八)
    Redis之Lua的应用(四)
    《长安的荔枝》读书笔记
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8351413.html
Copyright © 2020-2023  润新知