hdu1536 S-Nim 博弈sg模板题 两种写法(递归+!递归)

冷暖自知 提交于 2019-11-27 15:45:52

LINK

题目描述

给定n堆石子以及一个由k个不同正整数构成的数字集合S。

现在有两位玩家轮流操作,每次操作可以从任意一堆石子中拿取石子,每次拿取的石子数量必须包含于集合S,最后无法进行操作的人视为失败。

问如果两人都采用最优策略,先手是否必胜。

数据范围

1≤n,k≤100,
1≤si,hi≤10000

样例

2 2 5
1
3 2 4 7

输出

W

定理1:对于集合S,mex(S)=mex({x1,x2…})=S中 没有出现的 最小非负整数
定理2.1:sg(n)=mex({sg(i1),sg(i2),sg(i3)…})。 n为结点;i1,i2,i3…是n的后继结点
定理2.2:sg(G)=sg(head). G是一个有向图,head是G的头结点。
定理3:sg(G1)sg(G2)sg(G3)sg(Gn)为n个有向图的异或和,对于n个有向图游戏,这个异或和就是它的答案(和nim类似,这个定理感兴趣可以去证明)

算法1:递归

C++ 代码

#include <bits/stdc++.h>
using namespace std;
int s[110], k, m, l, x, sg[11000], ans;
//这个题目的有向图可以看成一个树,叶子结点就是必败结点值为0。非叶子结点存放mex({子节点的值})。
int sg_dfs(int n)
{
    bool mex[110]; //对于每个结点n都开一个数组mex 记录他的后继结点
    memset(mex, 0, sizeof(mex));
    if (sg[n] != -1)
        return sg[n];
    if (n - s[0] < 0) //最小的都不可以取,这个n没有后继了,数组中标记为0.
        return sg[n] = 0;
    for (int i = 0; i < k && (n >= s[i]); i++)
        mex[sg_dfs(n - s[i])] = 1; //n的后继标记为1
    for (int i = 0;; i++)
        if (!mex[i]) //mex集合中!没!出现过的最小的非负整数
            return sg[n] = i;
}
int main()
{
    ios::sync_with_stdio(false);
    while (cin >> k, k)
    {
        memset(sg, -1, sizeof(sg));
        sg[0] = 0;
        for (int i = 0; i < k; i++)
            cin >> s[i]; //s集合的值(每一步可以取得数)
        sort(s, s + k);
        cin >> m;
        while (m--)
        {
            cin >> l; //l堆
            ans = 0;
            for (int i = 0; i < l; i++)
            {
                cin >> x;
                ans ^= sg_dfs(x); //将每个堆当成一个有向图 最终答案为sg(x)异或和
            }
            if (ans)
                cout << 'W';
            else
                cout << 'L';
        }
        cout << endl;
    }
}

算法2 非递归

道理一样,写法不同,先初始化了sg数组。

C++ 代码

#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
int SG[N], k, m, x, l, ans;
int a[110];
//返回集合s中的最小非负整数
int mex(int s[], int n)
{
    if (n == 0)
        return 0; //没有后继
    sort(s, s + n);
    n = unique(s, s + n) - s;
    for (int i = 0;; i++)
        if (i >= n || s[i] != i)
            return i;
}
//eg:x的后继为x-a[0],x-a[1],,,,,x-a[n-1];
int sg(int x)
{
    int cnt = 0;
    int b[N];
    for (int i = 0; i < k; i++)
        if (x >= a[i])
            b[cnt++] = SG[x - a[i]];
    return mex(b, cnt);
}
int main()
{
    ios::sync_with_stdio(false);
    while (cin >> k, k)
    {
        memset(SG, 0, sizeof(SG));
        for (int i = 0; i < k; i++)
            cin >> a[i]; //s集合的值(每一步可以取得数)
        sort(a, a + k);
        for (int i = 1; i < N; i++)
            SG[i] = sg(i);
        cin >> m;
        while (m--)
        {
            cin >> l; //l堆
            ans = 0;
            for (int i = 0; i < l; i++)
            {
                cin >> x;
                ans ^= SG[x]; //将每个堆当成一个有向图 最终答案为sg(x)异或和
            }
            if (ans)
                cout << 'W';
            else
                cout << 'L';
        }
        cout << endl;
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!