hihocoder #1173 : 博弈游戏·Nim游戏·三

106 篇文章 0 订阅
96 篇文章 0 订阅


#1173 : 博弈游戏·Nim游戏·三

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB
描述

在这一次游戏中Alice和Bob决定在原来的Nim游戏上增加一条规则:每一次行动时,不仅可以选择一堆取走任意数量的石子(至少取1颗,至多取出这一堆剩下的所有石子),还可以选择将一堆石子分成两堆石子,但并不取走石子。比如说有一堆石子为k个,当Alice或者Bob行动时,可以将这一堆石子分成两堆,分别为x,y。满足x+y=k,x,y>0。那么增加了这一条规则后,在Alice总先手的情况下,请你根据石子堆的情况判断是Alice会获胜还是Bob会获胜?

提示:Sprague-Grundy

输入

第1行:1个整数N。表示石子堆数。1≤N≤100
第2行:N个整数,第i个整数表示第i堆石子的个数A[i],1≤A[i]≤20000

输出

第1行:1个字符串,若Alice能够获胜输出"Alice",否则输出"Bob"

样例输入
3
1 2 4
样例输出
Bob

这道题需要用到sg函数,对于每堆石子,假设大小为x,它的后继有1,2,3...x-1已经(1,x-1),(2,x-2)..根据sg定理(1,x-1)的sg值就是sg(1)^sg(x-1),所以x的后继的sg值就是(这题由于有分堆的操作,所以sg(x)就不一定等于x了)sg(1)、sg(2)、sg(3)...以及sg(1)^sg(x-1)、sg(2)^sg(x-2)...的集合,这样我们就看求出每个x的sg值了。

但是x的上限是20000,显然不能完全打表求sg值,可以通过找sg值的规律解题。

先打出1到100的sg值:

1 2 4 3 5 6 8 7 9 10
12 11 13 14 16 15 17 18 20 19
21 22 24 23 25 26 28 27 29 30
32 31 33 34 36 35 37 38 40 39
41 42 44 43 45 46 48 47 49 50
52 51 53 54 56 55 57 58 60 59
61 62 64 63 65 66 68 67 69 70
72 71 73 74 76 75 77 78 80 79
81 82 84 83 85 86 88 87 89 90
92 91 93 94 96 95 97 98 100 99

我们很容易发现奇数对的数都是本身,比如1,2和5,6,而偶数位的数都发生了交换,比如sg(3)=4,sg(4)=3,这样我们就找到规律了,可以根据规律得到子游戏的sg值,最后求一下异或和就可以做出


代码:

#include <cstdio>
#include <iostream>
using namespace std;
const int maxn = 20000;
int sg[maxn+5];
int book[maxn+6];
void init()
{
    int i, j;
    for(i=1; i<=100; i++)
    {
        for(j=1; j<=100; j++)book[j]=0;
        for(j=1; j<i; j++)
        {
            book[sg[j]]=1;
            book[sg[j]^sg[i-j]]=1;
        }
        for(j=1; j<=1000; j++)if(book[j]==0)break;
        sg[i]=j;
    }
    j=0;
    for(i=1; i<=100; i++)
    {
        printf("%d ", sg[i]);
        j++;
        if(j%10==0)printf("\n");
    }
}
int main()
{
//    init();
    int i, j;
    int n, x, sg=0;
    cin>>n;
    for(i=0; i<n; i++)
    {
        scanf("%d", &x);
        if(((x+(x&1))>>1)&1)sg^=x;
        else sg^=(x&1)?x+1:x-1;
    }
    if(sg)printf("Alice\n");
    else printf("Bob\n");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值