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

来源:互联网 发布:手机号码扫号软件 编辑:程序博客网 时间:2024/05/21 22:30


#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"

样例输入
31 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");}


0 0
原创粉丝点击