题目描述
两个人玩取球的游戏。
一共有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 -
一看到博弈问题自然而然想到nim博弈,就是找一个简单方法,但是他和自己了解的三种博弈也不一样,就束手无策了。但看了题解后发现,自己太害怕博弈,这一个应该用到的不算是博弈的思想吧,因为博弈的解法都感觉不是小白可以想到的,而这一个其实很简单。
我觉得简单之处在于他给了我们能拿走的数量,这里和nim博弈不同,因为我们可以拿的数量是固定的,所以就可以递归了嘛,一开始我可以拿n1,n2,n3,那么就从这里递归,但其实还是有巧妙之处的。
一开始假设我们设dfs(me,you,num),me表示我手中的数目,you表示对方手中的数目,num表示此时还剩余的数目,
- 在第一次选了之后dfs(me + x,you,num-x),但是下一次是由选了,如果这样递归的话就一直是我选,其实我的想法是设一个flag数组来判断要谁选,但是题解是这样搞的,选了之后dfs(you,me + x,num-x),这样每次都是交替选了。
- dfs,一半我们会考虑记忆性递归,用一个数组保存结果,但是题解给出了一个巧妙的地方,因为我们最后只看奇偶性,即便对于一个局面还剩余同样的石子未选时,只要me和you的奇偶性是一样的,那么他们的局面就相当于一样的,就可以直接返回结果了。
- 所以dfs(me,you,num),me和you分别用0,1表示奇偶性,然后设数组dp[num][i][j] = new dp[n][2][2]num表示此时石子剩余数量,i表示me的奇偶性,j表示you的奇偶性,那么递归时dfs变成了dfs(n - a[i], you, (me + a[i]) % 2)。
- dfs的退出条件必然是不能再选的时候,也就是num的数目小于n1,n2,n3中的最小值,此时判断me和you的奇偶性,奇偶性相同,就是平局,me是奇则获胜,否则则失败。
- 怎么判断最终的结果,因为“能获胜则输出+,次之,如有办法逼平对手,输出0,无论如何都会输,则输出-”所以,只要在递归的过程中出现了一次获胜,那么最终会获胜,如果出现了一次平局,那么最终平局,否则只能失败。
- 但是我有一个疑问,为什么题解中
if (temp == '-') {
book[n][me][you] = '+';
return '+';
}
不应该是temp == '+‘的时候获胜吗?其实也不然
7. 还有一个疑问就是,因为我每次都会调换me和you的位置那么最后判断退出的时候的me真的是我手中的石子吗?
8. 我懂了我懂了,之所以temp=’-'的时候判断为赢,是因为它的dfs中me和you是左右互换的,在dfs中返回了+,代表me赢了,返回它的上一层循环时,恰恰是me输了,所以也不用判断最后me到底是不是真正的我。
AC代码
import java.util.Arrays;
import java.util.Scanner;
public class okt9取球博弈 {
static int[] a;
static char[][][] book = new char[1000][2][2];
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
a = new int[3];
int[] b = new int[5];
for (int i = 0; i < 3; i++) {
a[i] = scanner.nextInt();
}
Arrays.sort(a);
for (int i = 0; i < 5; i++) {
b[i] = scanner.nextInt();
System.out.print(dfs(b[i], 0, 0) + " ");
}
}
private static char dfs(int n, int me, int you) {
if (n < a[0]) {
if (me == you) {
return book[n][me][you] = '0';
}
return me == 1 ? (book[n][me][you] = '+') : (book[n][me][you] = '-');
// return (me & 1) == 1 ? (book[n][me][you] = '+') : (book[n][me][you] = '-');
}
if (book[n][me][you] != '\0') {
return book[n][me][you];
}
int flag = 0;
for (int i = 0; i < 3; i++) {
if (a[i] <= n) {
char temp = dfs(n - a[i], you, (me + a[i]) % 2);
// if (temp == '+') {
// book[n][me][you] = '+';
// return '+';
// }
if (temp == '-') {
book[n][me][you] = '+';
return '+';
}
if (temp == '0') {
flag = 1;
}
}
}
if (flag == 1) {
book[n][me][you] = '0';
return '0';
} else {
book[n][me][you] = '-';
return '-';
}
}
}