hdu5795A Simple Nim/hihoCoder1173+SG博弈

Problem Description
Two players take turns picking candies from n heaps,the player who picks the last one will win the game.On each turn they can pick any number of candies which come from the same heap(picking no candy is not allowed).To make the game more interesting,players can separate one heap into three smaller heaps(no empty heaps)instead of the picking operation.Please find out which player will win the game if each of them never make mistakes.

Input
Intput 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≤106,1≤s[i]≤109)

Output
For each test case,output a line whick contains either”First player wins.”or”Second player wins”.

Sample Input

2
2
4 4
3
1 2 4

Sample Output

Second player wins.
First player wins.

Author
UESTC

Source
2016 Multi-University Training Contest 6

题意:两个人轮流从n堆石子里取走任意个(至少一个),或则不拿走石子,但是讲石子分成3堆。每堆至少为一个。最后没有石子去的人输。对于如果没有第二种分石子情况就是nim博弈,但是加了第二种状态后就不是了。因此对于每一堆石子都因该有一个SG值,因此直接对一堆石子打表SG然后找下规律就可以发现。当x%8==0 sg=x-1,当x%8==7 sg=x+1,其他的sg=x。所以所有堆sg亦或就是答案。

#include<cstdio>
#include<cstring>
using namespace std;

int SG[10006];
int getSG(int x){  //针对于一堆而言
    if(SG[x]!=-1) return SG[x];
    if(x==0) return 0; //0个可以输
    if(x==1) return 1; //1个可以赢

    int visit[10006];
    memset(visit,0,sizeof(visit));

    for(int i=0;i<x;i++) visit[getSG(i)]=1; //当成一堆时的可到状态

    for(int i=1;i<x;i++){  //分成3堆
        for(int j=i;j<x;j++){
            for(int k=j;k<x;k++){
                if(i+j+k==x){ //子问题的亦或是可到状态
                    visit[getSG(i)^getSG(j)^getSG(k)]=1;
                }
            }
        }
    }

    for(int i=0;;i++){
        if(visit[i]==0){
            return i;
        }
    }
}
int n;
int x;
int main(){
    /*
    memset(SG,-1,sizeof(SG));
    for(int i=1;i<100;i++){
        SG[i]=getSG(i);
        printf("%2d:%d\n",i,SG[i]);
    }*/

    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        int ans=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&x);
            if(x%8==0) ans^=(x-1);
            else if(x%8==7) ans^=(x+1);
            else ans^=x;
        }
        if(ans) printf("First player wins.\n");
        else printf("Second player wins.\n");
    }
    return 0;
}

hihocoder的1173.。问题更简单。只分成两堆。思路一样。。打个表观察下。

描述

在这一次游戏中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
#include<cstdio>
#include<cstring>
using namespace std;

int SG[10006];
int getSG(int x){  //针对于一堆而言
    if(SG[x]!=-1) return SG[x];
    if(x==0) return 0; //0个可以输
    if(x==1) return 1; //1个可以赢

    int visit[10006];
    memset(visit,0,sizeof(visit));

    for(int i=0;i<x;i++) visit[getSG(i)]=1; //当成一堆时的可到状态

    for(int i=1;i<x;i++){  //分成3堆
        for(int j=i;j<x;j++){
            if(i+j==x){ //子问题的亦或是可到状态
                visit[getSG(i)^getSG(j)]=1;
            }
        }
    }

    for(int i=0;;i++){
        if(visit[i]==0){
            return i;
        }
    }
}
int n;
int x;
int main(){

    /*memset(SG,-1,sizeof(SG));
    for(int i=1;i<100;i++){
        SG[i]=getSG(i);
        printf("%2d:%d\n",i,SG[i]);
    }*/


    scanf("%d",&n);
    int ans=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        if(x%4==0) ans^=(x-1);
        else if(x%4==3) ans^=(x+1);
        else ans^=x;
    }
    if(ans) printf("Alice\n");
    else printf("Bob\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值