hdu4111Alice and Bob+SG博弈

Description
Alice and Bob are very smart guys and they like to play all kinds of games in their spare time. The most amazing thing is that they always find the best strategy, and that’s why they feel bored again and again. They just invented a new game, as they usually did.
The rule of the new game is quite simple. At the beginning of the game, they write down N random positive integers, then they take turns (Alice first) to either:
1. Decrease a number by one.
2. Erase any two numbers and write down their sum.
Whenever a number is decreased to 0, it will be erased automatically. The game ends when all numbers are finally erased, and the one who cannot play in his(her) turn loses the game.
Here’s the problem: Who will win the game if both use the best strategy? Find it out quickly, before they get bored of the game again!

Input
The first line contains an integer T(1 <= T <= 4000), indicating the number of test cases.
Each test case contains several lines.
The first line contains an integer N(1 <= N <= 50).
The next line contains N positive integers A 1 ….A N(1 <= A i <= 1000), represents the numbers they write down at the beginning of the game.

Output
For each test case in the input, print one line: “Case #X: Y”, where X is the test case number (starting with 1) and Y is either “Alice” or “Bob”.

Sample Input

3
3
1 1 2
2
3 4
3
2 3 5

Sample Output

Case #1: Alice
Case #2: Bob
Case #3: Bob

Source
2011 Asia ChengDu Regional Contest
题意:有一堆的数字,两种操作:1:将某一个数值减1、2:将两个数字合并
解法:SG[i][j]表示有i个石子数为1的堆数,其它堆合并再取完的步数为j。直接SG扫就好了。。。
有几个地方重点说一下,我最开始也是没理解,后来懂了。。
(1)if(one>=1&&!getSG(one-1,sumstep)) return SG[one][sumstep]=1;
比赛时和菊苣手动模拟了一下,其实每一种状态,不管是怎样合并或取数只要子状态里出现了先手必输的状态,因此该状态可以转移到让对手必输,所以自己必赢。。。(也就只有这个点比赛时发现了)
(2) if(one>=1&&sumstep>=1&&!getSG(one-1,sumstep+1)) return SG[one][sumstep]=1;
将一个1合并到非1的上去,其他的操作步数为什么时+1,而不是2(合并+去掉),因为+2其实就是他本身的等价状态并不是子状态。+1(去掉)才是子状态。同理类推第四种情况的时候。

/*
#include<bits/stdc++.h>
using namespace std;
int SG[55][55000];
//SG[i][j]表示有i个石子数为1的堆数,其它堆合并再取完的步数为j。
//若值为1则先取者胜,为0为先取者输
int getSG(int one,int sumstep){
    if(SG[one][sumstep]!=-1) return SG[one][sumstep];
    //没有1的时候就是各取一个
    if(one==0&&sumstep%2==1) return SG[one][sumstep]=1;
    else if(one==0&&sumstep%2==0) return SG[one][sumstep]=0;
    //当其他的步数为1时,
    if(sumstep==1) return SG[one][sumstep]=getSG(one+1,0);

    //只要找到一种是输的其他状态。那他就能赢
    SG[one][sumstep]=0;
    //1的个数去掉一个
    if(one>=1&&!getSG(one-1,sumstep)) return SG[one][sumstep]=1;
    //1的个数去掉一个
    if(sumstep>=1&&!getSG(one,sumstep-1)) return SG[one][sumstep]=1;
    //1的合并到非1的上去
    if(one>=1&&sumstep>=1&&!getSG(one-1,sumstep+1)) return SG[one][sumstep]=1;
    //将两个1的合并
    if(one>=2){
        //1的为0,相当于将一个1加到另一个1上,
        if(sumstep==0&&!getSG(one-2,sumstep+2)) return SG[one][sumstep]=1;
         //1的为0,相当于将两个1加到非1上,
        else if(sumstep!=0&&!getSG(one-2,sumstep+3)) return SG[one][sumstep]=1;
    }
    return SG[one][sumstep];
}

int main(){
    int t;
    scanf("%d",&t);
    memset(SG,-1,sizeof(SG));
    for(int cas=1;cas<=t;cas++){
        int n;
        int one=0,sumstep=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            if(x==1) one++;
            else sumstep+=x+1;//合并,去掉
        }
        if(sumstep) sumstep--; //合并为p-1次,
        if(getSG(one,sumstep)) printf("Case #%d: Alice\n",cas);
        else printf("Case #%d: Bob\n",cas);
    }
    return 0;
}

*/
//按模板来写一个。。。
#include<bits/stdc++.h>
using namespace std;
int SG[55][55000];
int getSG(int one,int sumstep){
    if(SG[one][sumstep]!=-1) return SG[one][sumstep];
    if(one==0&&sumstep%2==1) return SG[one][sumstep]=1;
    else if(one==0&&sumstep%2==0) return SG[one][sumstep]=0;

    if(sumstep==1) return SG[one][sumstep]=getSG(one+1,0);

    //SG[one][sumstep]=0;
    bool visit[2];
    memset(visit,false,sizeof(visit));

    if(one>=1) visit[!getSG(one-1,sumstep)]=true;
    if(sumstep>=1)visit[!getSG(one,sumstep-1)]=true;
    if(one>=1&&sumstep>=1) visit[!getSG(one-1,sumstep+1)]=true;
    if(one>=2){
        if(sumstep==0) visit[!getSG(one-2,sumstep+2)]=true;
        else visit[!getSG(one-2,sumstep+3)]=true;
    }
    if(visit[1]==true) return SG[one][sumstep]=1;//推出来的
    else return SG[one][sumstep]=0;
}

int main(){
    int t;
    scanf("%d",&t);
    memset(SG,-1,sizeof(SG));
    for(int cas=1;cas<=t;cas++){
        int n;
        int one=0,sumstep=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            if(x==1) one++;
            else sumstep+=x+1;//合并,去掉
        }
        if(sumstep) sumstep--; //合并为p-1次,
        if(getSG(one,sumstep)) printf("Case #%d: Alice\n",cas);
        else printf("Case #%d: Bob\n",cas);
    }
    return 0;
}


/*
3
3
1  1  2
2
3  4
3
2  3  5



Case  #1:  Alice
Case  #2:  Bob
Case  #3:  Bob
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值