蓝桥杯java 取球博弈

文章探讨了一个两人取球游戏,每次可以从给定的数目中取球,目标是分析当双方都采取最优策略时,首个取球者是否一定能够赢得比赛。文章通过编程实现动态规划和深度优先搜索算法来模拟并确定结果。

题目描述

两个人玩取球的游戏。
一共有N个球,每人轮流取球,每次可取集合{n1,n2,n3}中的任何一个数目。
如果无法继续取球,则游戏结束。
此时,持有奇数个球的一方获胜。
如果两人都是奇数,则为平局。

假设双方都采用最聪明的取法,
第一个取球的人一定能赢吗?
试编程解决这个问题。

输入格式:
第一行3个正整数n1 n2 n3,空格分开,表示每次可取的数目 (0<n1,n2,n3<100)
第二行5个正整数x1 x2 … x5,空格分开,表示5局的初始球数(0<xi<1000)

输出格式:
一行5个字符,空格分开。分别表示每局先取球的人能否获胜。
能获胜则输出+,
次之,如有办法逼平对手,输出0,
无论如何都会输,则输出-

例如,输入:
1 2 3
1 2 3 4 5
程序应该输出:
+ 0 + 0 -

再例如,输入:
1 4 5
10 11 12 13 15
程序应该输出:
0 - 0 + +

再例如,输入:
2 3 5
7 8 9 10 11
程序应该输出:
+ 0 0 0 0

代码

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

public class Main {  
    public static void main(String[] args) {  
        new Solution();  
    }  
}  
class Solution{  
    Scanner scanner = new Scanner(System.in);  
    int[] table = new int[3];  
    int[] balls = new int[5];  
    char[][][] cache = new char[1000][2][2];  
    public Solution() {  
        for (int i = 0; i < 3; i++) table[i] = scanner.nextInt();  
        Arrays.sort(table);  
          
        for (int i = 0; i < 5; i++) balls[i] = scanner.nextInt();  
          
        for (int i = 0; i < 5; i++) System.out.print(dfs(balls[i], 0, 0) + " ");  
    }  
    public char dfs(int nums, int me, int you) {  
        if (cache[nums][me][you] != 0) return cache[nums][me][you];  
          
        if (nums < table[0])  
            if (me == 1)  
                if (you == 1)  
                    return cache[nums][1][1] = '0';  
                else  
                    return cache[nums][1][0] = '+';  
            else  
                if (you == 1)  
                    return cache[nums][0][1] = '-';  
                else  
                    return cache[nums][0][0] = '0';  
                  
        boolean isTie = false;  
        for (int i = 0; i < 3; i++)   
            if (nums >= table[i]) {  
                char res = dfs(nums - table[i], you, (me + table[i]) % 2);  
                if (res == '-') return cache[nums][me][you] = '+';  
                if (res == '0') isTie = true;  
            }  
          
        if (isTie) return cache[nums][me][you] = '0';  
        return cache[nums][me][you] = '-';  
    }  
}

题解

以为此例

1 2 3
1 2 3 4 5

1

+(先拿1个球) 

2

0
(先拿两个球)

3

+(先拿3个球)

4

0(无论先拿几个球最好的情况都是0)

5

-(先拿1 ->4个球的情况 对方可以选择以奇数平 -> 输
   先拿3 对方选择以奇数平 -> 输
   先拿2个 相当于3个对方先手 ->)

如此我们可以看出
总球数x 其输赢情况可以从 x - n1, x - n2, x - n3的情况中择优挑选 (有三种情况,只选其中对自己有利的)

用dfs算法可以进行模拟
用于递归函数参数表 dfs(nums 剩余数, chess 先后方球数, remote 后手球数)

用dfs(nums - xi, remote, chess - xi) 返回的结果进行择优选取

import java.util.Arrays;  
import java.util.Scanner;  
  
public class Main {  
    public static void main(String[] args) {  
        new Solution();  
    }  
}  
class Solution{  
    Scanner scanner = new Scanner(System.in);  
    int[] table = new int[3];  
    int[] balls = new int[5];  
  
    public Solution() {  
        for (int i = 0; i < 3; i++) table[i] = scanner.nextInt();  
        Arrays.sort(table);  
        for (int i = 0; i < 5; i++) balls[i] = scanner.nextInt();  
        for (int i = 0; i < 5; i++) System.out.print(dfs(balls[i], 0, 0) + " ");  
    }  
    public char dfs(int nums, int chess, int remote) {  
        if (nums < table[0])  
            if (chess % 2 == 1)  
                if (remote % 2 == 1) return '0';  
                else return '+';  
            else  
            if (remote % 2 == 1) return '-';  
            else return '0';  
        boolean isTie = false;  
        for (int i = 0; i < 3; i++) {  
            if (nums >= table[i]) {  
                char res = dfs(nums - table[i], remote, chess + table[i]);  
                if (res == '-') return '+';  
                if (res == '0') isTie = true;  
            }  
        }  
        if (isTie) return '0';  
        return '-';  
    }  
}

如果数据量太大, 会发生栈超时的情况, 所以我们要进行剪枝
可以选择的方法是 约定chess remote只保存奇偶
在递归的过程中如果存在(nums, chess, remote)都一样的函数调用况那么这 两个函数返回的结果也是一样的
为了避免二次计算, 我们选择将第一次计算结果保存在一个三维数组中, cache[球数][奇偶][奇偶]
这样只需要在入栈的时候判断是否已存在结果 存在就直接返回即可, 避免二次计算
参见上述代码

好的!本次分享到这就结束了
如果对铁汁你有帮助的话,记得点赞👍+收藏⭐️+关注➕
我在这先行拜谢了:)

思路参考:
https://wmathor.com/index.php/archives/1266/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值