蓝桥杯——取球博弈(JAVA)

题目:

两个人玩取球的游戏。

一共有 N 个球,每人轮流取球,每次可取集合 n1​,n2​,n3​中的任何一个数目。

如果无法继续取球,则游戏结束。

此时,持有奇数个球的一方获胜。

如果两人都是奇数,则为平局。

假设双方都采用最聪明的取法,

第一个取球的人一定能赢吗?

试编程解决这个问题。

输入描述

输入格式:

第一行 3 个正整数n1​,n2​,n3​ (0<n1​,n2​,n3​<100),空格分开,表示每次可取的数目。

第二行 5 个正整数 x1​,x2​,⋯,x5​ (0<xi​<1000),空格分开,表示 5 局的初始球数。

输出描述

输出一行 5 个字符,空格分开。分别表示每局先取球的人能否获胜。

能获胜则输出 +,次之,如有办法逼平对手,输出 0,无论如何都会输,则输出 -。

输入输出样例

示例

输入

1 2 3
1 2 3 4 5

输出

+ 0 + 0 -

运行限制

  • 最大运行时间:3s
  • 最大运行内存: 256M

初始代码(会超时):

import java.util.Arrays;
import java.util.Scanner;

public class qu_qiu_bo_yi {
    static int[] N;                     //全局变量存放可取集合
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        N=new int[3];
        int[] A=new int[5];              //存放5局的数目
        char[] result=new char[5];       //存放五局的结果
        for (int i=0;i<3;i++){
            N[i]=sc.nextInt();
        }
        Arrays.sort(N);
        for (int i=0;i<5;i++){
           A[i]=sc.nextInt();
           result[i]=f(A[i],0,0);
        }
        for (int i=0;i<5;i++){
            System.out.print(result[i]+" ");
        }
        sc.close();

    }
    public static char f(int num,int me,int you){  //方法递归求结果,num:总球数,me:我手中的球数,you:对手手中的球数。
        if (num<N[0]){                    //数组N已经过升序排列N[0]为最小的可取球数,若num<N[0]则说明不能再取球了游戏结束,判断输赢。
            if ((me&1)==1&&(you&1)==0){
                return '+';               //用位运算(下面会解释)判断奇数偶数并输出结果
            }
            if ((me&1)==0&&(you&1)==1){
                return '-';
            }
            else {
                return '0';
            }
        }
        boolean ping=false;             //用于记录是否存在平局
        for (int i = 0; i <3 ; i++) {
            if (num >= N[i]){
               char result= f(num-N[i],you,me+N[i]);  //因为是两个人博弈问题,所以每摸一次球就要换对方摸球,即me和you位置进行交换。
               if (result=='-'){
                   return '+';          //因为我们总是可以采取最聪明的方法所以只要对手有一种输的可能我们就必赢
               }
               if (result=='0'){
                   ping=true;           //如果出现平局先记录下来
               }
            }
        }
        if (ping==true){                //如果可以走出循环走到这,那么就只有平局或必输,这里我们判断平局,ping为true则有平局的可能,因为我们总是可以采取最聪明的方法所以一定平局。
            return '0';
        }
        else {                       //不赢也没办法平局则必输
            return '-';
        }
    }
}

 首先解释一下位运算:&运算符有两种用法:一种是为运算符;一种是逻辑运算符;

作为位运算符是进行二进制运算的,当两个对应位上都为1的时候才为1,否则为0

因为偶数二进制最后一位一定是0奇数最后一位一定是1,所以某个数n&1==1则n为奇数n&1==0则n为偶数。

比如2&1 相当于010&001结果为000=0为偶数

3&1相当于011&001结果为001=1为奇数。

 我们再来说一下为什么会超时:因为xi最大为1000假设三个可取集合全为1,2,3则三个集合都可取三个集合取得的结果又有三种取法

 如图会有3^n种结果。

就会造成超时,那么我们就要想办法改进,这里要用到“记忆递归”顾名思义就是在其中的某个结点记录下它走到最后的结果,那么当在后面结点中遇到同样的情况就不用再次递归了。

代码如下:

import java.util.Arrays;
import java.util.Scanner;

public class qu_qiu_bo_yi {
    static int[] N;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        N=new int[3];
        int[] A=new int[5];
        char[] result=new char[5];
        for (int i=0;i<3;i++){
            N[i]=sc.nextInt();
        }
        Arrays.sort(N);
        for (int i=0;i<5;i++){
           A[i]=sc.nextInt();
           result[i]=f(A[i],0,0);
        }
        for (int i=0;i<5;i++){
            System.out.print(result[i]+" ");
        }
        sc.close();

    }                                           //主函数没有改变
    private static char[][][] rem=new char[1000][2][2];  //用一个char类型的三维数组存储结果
    public static char f(int num,int me,int you){     //这里me和you的含义改变了,因为我们并不需要知道两个人手中到底有多少颗求,只需要知道奇偶就可以判断结果,所以me:我手中球是奇数还是偶数,you:对手手中的球是奇数还是偶数。

        if (num<N[0]){
            if ((me&1)==1&&(you&1)==0){
                return '+';
            }
            if ((me&1)==0&&(you&1)==1){
                return '-';
            }
            else {
                return '0';
            }
        }                               //同第一次代码判断奇偶输出结果
        if (rem[num][me][you]!='\0'){
            return rem[num][me][you];
        }                              //在进行递归前先看rem三维数组中是否存在这种情况,如果有直接返回,没有继续递归。
        boolean ping=false;
        for (int i = 0; i <3 ; i++) {
            if (num >= N[i]){
               char result= f(num-N[i],you,(N[i]&1)==0?me:1-me); //判断取的球是奇数还是偶数,偶数的话还是0,奇数则变为1。因为只有0,1两种情况所以三维数组才可以定义为[1000][2][2]最多只有四千种情况。
               if (result=='-'){
                   rem[num][me][you]='+';   //记录这种情况的结果
                   return '+';
               }
               if (result=='0'){

                   ping=true;
               }
            }
        }
        if (ping==true){
            rem[num][me][you]='0';           //记录这种情况的结果
            return '0';
        }
        else {
            rem[num][me][you]='-';           //记录这种情况的结果 
            return '-';
        }
    }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值