拆分-Nim游戏

拆分-Nim游戏

题目描述

在这里插入图片描述


核心思路

题目大意就是拿走一堆放回两堆但是两堆(任意一堆)的数量是不能大于拿走的那一堆的,即每一堆可以变成不大于原来那堆的任意大小的两堆

a [ i ] a[i] a[i]可以拆分为 ( b [ i ] , b [ j ] ) (b[i],b[j]) (b[i],b[j]),为了避免重复,我们规定 b [ i ] ≥ b [ j ] b[i]\geq b[j] b[i]b[j],即有 a [ i ] ≥ b [ i ] ≥ b [ j ] a[i]\geq b[i]\geq b[j] a[i]b[i]b[j],相当于一个局面拆分成了两个局面,由SG函数理论,多个独立局面的SG值,等于这些局面SG值的异或和。

在这里插入图片描述


代码

#include<iostream>
#include<cstring>
#include<unordered_set>
using namespace std;
const int N=110;
int f[N];
int n;
int SG(int x)
{
    //记忆化搜索,如果已经求出了f[x],当再次需要求f[x]时,只需要返回即可
    if(f[x]!=-1)
        return f[x];
    unordered_set<int>S;    //哈希表用来存储所有能到的局面
    //要分成两堆,第一堆的个数取值范围是0到x-1   i用来枚举第一堆
    for(int i=0;i<x;i++)
    {
        //为了避免重复,我们约定第二堆的个数小于等于第一堆的个数 因此j<=i
        //j用来枚举第二堆
        for(int j=0;j<=i;j++)
        {
            //相当于一个局面拆分成了两个局面,由SG函数理论,多个独立局面的SG值,等于这些局面SG值的异或和
            S.insert(SG(i)^SG(j));
        }
    }
    //上面已经求出了当前节点x它所有后继节点的SG函数值,得到了Mex集合
    //那么我们就只要取遍历这个Mex集合然后找出最小的没有出现在这个集合中的非负整数即可
    //那么这个非负整数就是当前节点x的SG函数值
    for(int i=0; ;i++)
    {
        if(!S.count(i))//没有出现在Mex集合中
        {
            return f[x]=i;  //当前节点x的SG函数值就是这个非负整数i
        }
    }
}
int main()
{
    //记忆化搜索需要把初始状态置为-1 表示所有数都还没有被计算出来
    memset(f,-1,sizeof f);
    scanf("%d",&n);
    int res=0;  //答案
    while(n--)
    {
        int x;
        scanf("%d",&x);
        res^=SG(x);     //异或值
    }
    //如果最终异或结果不为0,则说明先手胜利
    if(res)
        puts("Yes");
    //如果最终异或结果为0,则说明先手输
    else
        puts("No");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值