hdu 3537 Daizhenyang's Coin 博弈(Nim游戏+sg定理+找规律)

题意:已知一排硬币中有n个硬币正面朝上,输入正面朝上的硬币的位置ai。两人轮流操作,每次操作可以翻转1,2,或则3枚硬币,其中翻转的最右的硬币必须是正面朝上的,最后不能翻转的为负。理解题意理解了半天,还差点理解错了。。

题解:我们发现这是个Nim游戏,简单化题目,就是将一个游戏分成多个子游戏。这题是分出的子游戏是,只有一个正面朝上的硬币,位置在ai,求出所有该类sg函数值sg[ai]。则答案就是ans=sg[a1]^sg[a2]^...^sg[an]。

接着就是求单个sg的函数值,由于ai是从0开始,而当ai=0时,只有翻转“正”到“反”(必败),所以sg[0]=1;之后可以for一遍求出之后的sg函数值了,由于ai必定变为反,所以后继状态必定是已知的sg函数值,详细见代码。

又ai很大,所以只能找规律。这个考验眼力和想象能力了。。sg[x]=2*x或则sg[x]=2*x+1。从这出发,我们又发现所有sg函数值二进制表示的数中1的个数为奇数个。这样就简单了,对于输入的ai,判断下2*ai的二进制表示中1的个数奇偶性就OK。

之后就是SG定理的运用,其实就是异或下,一切OK。。。。。



,题解代码:

#include <cstdio>
#include <cstring>
#include <map>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e6+10;
map<int,int>mm;
bool judge(int x)
{
    int s=0;
    while(x)
    {
        if(x&1)s++;
        x=x>>1;
    }
    if(s%2)return true;
    return false;
}
int find(int x)
{
    if(judge(2*x))return 2*x;
    return 2*x+1;
}
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        mm.clear();
        int ans=0,i,j,a;
        for(i=0;i<n;i++)
        {
            scanf("%d",&a);
            if(mm[a]==0)//去重
            ans=ans^find(a);
            mm[a]=1;
        }
        if(ans==0)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}


sg定理代码,sg函数值打表+找规律:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=1e4+10;
int sg[maxn],vis[maxn];
void init()
{
    int i,j,k;
    sg[0]=1;
    for(i=1;i<=1000;i++)
    {
        memset(vis,0,sizeof(vis));
        vis[0]=1;    //翻一个硬币,后继必败
        for(j=0;j<i;j++)
        vis[sg[j]]=1;   //翻两个硬币
        for(j=0;j<i;j++)
        for(k=0;k<j;k++)
        vis[sg[j]^sg[k]]=1;//翻三个硬币
        for(j=0;;j++)
        if(!vis[j])break;
        sg[i]=j;
    }
    for(i=1;i<=20;i++)
    cout<<i<<":"<<sg[i]<<endl;
}
int main()
{
    init();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值