HDU 2311-Nim or not Nim?(Nim博弈-打sg表找规律)

15 篇文章 0 订阅

Nim or not Nim?

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1493    Accepted Submission(s): 738


Problem Description
Nim is a two-player mathematic game of strategy in which players take turns removing objects from distinct heaps. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap.

Nim is usually played as a misere game, in which the player to take the last object loses. Nim can also be played as a normal play game, which means that the person who makes the last move (i.e., who takes the last object) wins. This is called normal play because most games follow this convention, even though Nim usually does not.

Alice and Bob is tired of playing Nim under the standard rule, so they make a difference by also allowing the player to separate one of the heaps into two smaller ones. That is, each turn the player may either remove any number of objects from a heap or separate a heap into two smaller ones, and the one who takes the last object wins.
 

Input
Input contains multiple test cases. The first line is an integer 1 ≤ T ≤ 100, the number of test cases. Each case begins with an integer N, indicating the number of the heaps, the next line contains N integers s[0], s[1], ...., s[N-1], representing heaps with s[0], s[1], ..., s[N-1] objects respectively.(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)
 

Output
For each test case, output a line which contains either "Alice" or "Bob", which is the winner of this game. Alice will play first. You may asume they never make mistakes.
 

Sample Input
  
  
2 3 2 2 3 2 3 3
 

Sample Output
  
  
Alice Bob
 

Source
 

Recommend
gaojie

题目意思:

两个人从n堆石子中取小石子或者将某一堆石子分成两堆,Alice先手,求谁能赢。

解题思路:

对sg值打表,根据找出的规律再写代码。
啊摔!(╯°Д°)╯( ┻━┻)!!一开始木有注意到庞大的数据量,还每次都调用函数求sg,再判断,果断超时超内存。。

先贴AC代码,解释在后面
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;

int main()
{
    int t,n,i,a,res;
    scanf("%d",&t);
    while(t--)
    {
        res=0;
        scanf("%d",&n);
        for(i=0; i<n; i++)
        {
           cin>>a;
            if(a%4==3) res^=(a+1);
            else if(a%4==0) res^=(a-1);
            else res^=a;
        }
        if(res) cout<<"Alice"<<endl;
        else cout<<"Bob"<<endl;
    }
    return 0;
}

打表函数:

n是从1~t,每打完一次n的表就系统暂停一次。当n=1/2/3/4时木有任何规律可循,当n越大,规律就越明显。
注意一点:sg[j]^sg[i-j];//是可以分成两堆的情况

#include<iostream>
#include<cstdio>
#include<set>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 10000010
int n;//n堆
int k;//可取情况的数目
int x[MAXN];//n堆分别的数目
int a[MAXN];//可取情况
int sg[MAXN];//等价于Nim中的石子数
void init()
{
    int cnt=0;
    for(int i=0; i<MAXN; ++i)
        a[i]=++cnt;
}
void solve()
{
    int Max=*max_element(x,x+n);
    sg[0]=0;//轮到自己还剩0枚时是必败态
    for(int i=1; i<=Max; ++i)
    {
        set<int>s;//存储当前所能到达状态的sg值
        for(int j=0; j<i; ++j)
            s.insert(sg[j]^sg[i-j]);//可以分成两堆
        for(int j=0; j<i; ++j)
            s.insert(sg[j]);//不分成两堆,直接取
        int cnt=0;//寻找当前状态的最小排斥值
        while(s.count(cnt)!=0)//返回值为cnt的元素个数
            ++cnt;
        sg[i]=cnt;
        cout<<i<<" "<<sg[i]<<endl;
    }
    /*int res=0;
    for(int i=0; i<n; ++i)
        res^=sg[x[i]];
    if(res)puts("Alice");//先手必胜
    else puts("Bob");*/
}

int main()
{
    int t;
    scanf("%d",&t);
    init();
    n=1;
    while(t--)
    {
        int cnt=0;
        for(int i=0; i<n; ++i)
            x[i]=++cnt;
        solve();
        ++n;
        system("pause");
    }
    return 0;
}



规律如下:

sg[4k+1]=4k+1

sg[4k+2]=4k+2

sg[4k+3]=4k+4

sg[4k+4]=4k+3

至此,SG值都知道了,就可以XOR求结果判断啦~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值