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

被折叠的 条评论
为什么被折叠?



