HDU 1536 S-Nim

题意:

给你可以一次取走的个数,和每堆的数量,求先手是否有必胜的方法,如果必胜则输出“W",没有则为”L"。

思路:

这是一道先求SG函数,然后利用SG函数来进行尼姆博弈的一道题。

SG函数是这样的:

每个点的SG函数都不等于能达到的前面点的SG值的最小非负整数值。(有点长,分开来就是,找到能一步到达到的SG点,把这些点的SG值放在集合D中,这个点的SG值就是不属于D点的值中最小的非负数。)

eg:

step[3] = 1, 2, 3(可以走1步、两步、三步)

点:      0 1 2 3 4 5 6 7 8 9 10

SG值:0 1 2 3 0 1 2 3 0 1  2

具体为什么:

首先得知道尼姆博弈中所有数异或的意义:

1.值等于0,说明此状态为必输。

2.值不为0,说明此状态为必胜。

3.在必胜状态下,通过适当的策略,可以使状态变为必输态(也就是把必输的情况留给了对手)

4.在必输状态下,不管通过什么策略,只能转化为必胜态(把必胜的局面留给对手)

5.在面对0的状态是必输态(通过题意)。

还是上面的例子,如果是 8 2,那么SG值异或就是 0 ^ 2 != 0,最终结果为胜,

如果是 8 4,那么SG值为 0 ^ 0 = 0,最终结果为输。

由此可以看到,凡是通过一定策略可以到达的点的SG值是不相等的,所以异或后不为0(必胜),

而不管通过什么策略都达不到的点(因为对手和你一样聪明,他会阻止你的,比如 8 -> 4就不行(自己想想)),SG值是相等的,异或后不为0(必输)

这就是SG+尼姆博弈的应用和具体意义。

Code:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<map>
#include<cctype>
#include<vector>

#define TEST

#define LL long long
#define Mt(f, x) memset(f, x, sizeof(f));
#define xep(i, n) for(int i = 0; i < (n); ++i)
#define rep(i, s, e) for(int i = (s); i <= (e); ++i)
#define dep(i, s, e) for(int i = (s); i >= (e); --i)
#ifdef TEST
    #define See(a) cout << #a << " = " << a << endl;
    #define See2(a, b) cout << #a << " = " << a << ' ' << #b << " = " << b << endl;
    #define debug(a, s, e){ rep(_i, s, e) {cout << a[_i] << ' '; }cout << endl;}
    #define debug2(a, s, e, ss, ee) rep(i_, s, e) {debug(a[i_], ss, ee);}
#else
    #define See(a) {}
    #define See2(a, b) {}
    #define debug(a, s, e) {}
    #define debug2(a, s, e, ss, ee) {}
#endif

const int MAX = 2e9;
const int MIN = -2e9;
const int PI = acos(-1.0);
const double eps = 1e-8;

using namespace std;

const int N = 10000 + 5;

int step[N], n;
int f[N];

int mex(int s)
{
    bool v[105] = {};
    for(int i = 0; i < n; ++i)
    {
        int t = s - step[i];
        if(t < 0)
        {
            break;
        }
        if(f[t] == -1)
        {
            f[t] = mex(t);
        }
        v[f[t]] = true;
    }
    for(int i = 0; ; ++i)
    {
        if(!v[i])
        {
            return i;
        }
    }
}

int main()
{
    while(~scanf("%d", &n) && n)
    {
        Mt(f, -1);
        f[0] = 0;
        for(int i = 0; i < n; ++i)
        {
            scanf("%d", &step[i]);
        }
        sort(step, step + n);
        int m;
        scanf("%d", &m);
        string ans = "";
        while(m--)
        {
            int g;
            scanf("%d", &g);
            int tem = 0;
            while(g--)
            {
                int a;
                scanf("%d", &a);
                if(f[a] == -1)
                {
                    f[a] = mex(a);
                }
                tem ^= f[a];
            }
            if(tem == 0)
            {
                ans += "L";
            }
            else
            {
                ans += "W";
            }
        }
        cout << ans << endl;
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值