蓝桥杯 2016-9 取球博弈

题目描述
两个人玩取球的游戏。
一共有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表示此时还剩余的数目,

  1. 在第一次选了之后dfs(me + x,you,num-x),但是下一次是由选了,如果这样递归的话就一直是我选,其实我的想法是设一个flag数组来判断要谁选,但是题解是这样搞的,选了之后dfs(you,me + x,num-x),这样每次都是交替选了。
  2. dfs,一半我们会考虑记忆性递归,用一个数组保存结果,但是题解给出了一个巧妙的地方,因为我们最后只看奇偶性,即便对于一个局面还剩余同样的石子未选时,只要me和you的奇偶性是一样的,那么他们的局面就相当于一样的,就可以直接返回结果了。
  3. 所以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)。
  4. dfs的退出条件必然是不能再选的时候,也就是num的数目小于n1,n2,n3中的最小值,此时判断me和you的奇偶性,奇偶性相同,就是平局,me是奇则获胜,否则则失败。
  5. 怎么判断最终的结果,因为“能获胜则输出+,次之,如有办法逼平对手,输出0,无论如何都会输,则输出-”所以,只要在递归的过程中出现了一次获胜,那么最终会获胜,如果出现了一次平局,那么最终平局,否则只能失败。
  6. 但是我有一个疑问,为什么题解中
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 '-';
		}
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值