取球博弈--蓝桥

取球博弈


两个人玩取球的游戏。
一共有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

资源约定:
峰值内存消耗(含虚拟机) < 256M

CPU消耗  < 3000ms



由于没接触过博弈,所以一开始自己把他当作一般的搜索做。。。自然而然结果是扯淡的。。。

它区别于一般搜索的原因是题目中一句话:双方都采用最聪明的取法

这就告诉我们,如果存在一种情况,我知道我必输,我就不可能按那种取法进行下去,我要尽可能往好了取,能赢就赢,不能赢也要平,所以不能直接搜穷举比赛结果来查是不是先抽者能赢,而是在搜索中体现出不同选择对于结果的影响,然后从中选择一种最有利于自身的选项。

因此,我用一个长度为3的数组来记录各种取法带来的结果,初始情况下值都是0,如果能赢则下标2的置1,能平下标1置1,会输则下标0置1.

这是每轮通过三个取法搜索带来的结果,那么如何选择最聪明的取法?

		if(tmp[2]==1)return 0;
		else if(tmp[1]==1) return 1;
		else return 2;
在每轮搜索结束,返回给上一层的时候,如果tmp[2]==1,说明可以赢,因此选这个最佳方案,那么对于上一轮的人来说,他上一轮取法导致了这一轮玩家可以赢,而这个玩家又选最优解,因此上一轮的必输,返回0;第二个就是平局情况;第三个是无可奈何,毫无胜算,只能返回2让上一轮的人赢。

完整代码:

import java.util.*;
class Node{
	int num,a,b,flag;
	public Node(int num,int a,int b,int flag){
		this.num=num;this.a=a;this.b=b;this.flag=flag;
	}
}
public class Main {
	static int [] p=new int[3];
	static int dfs(Node node,int step){
		int tmp[]={0,0,0};
		if(node.num<p[0]){
//			System.out.println(node.num+" "+node.a+" "+node.b);
			if(node.flag==-1){
				if(node.a%2!=0&&node.b%2==0) return 2;
				else if(node.a%2==0&&node.b%2!=0) return 0;
				else return 1;}
			else {
				if(node.a%2!=0&&node.b%2==0) return 0;
				else if(node.a%2==0&&node.b%2!=0) return 2;
				else return 1;
			}
		}
		for(int i=0;i<3;i++){
			if(node.num>=p[i]){
				if(node.flag==1) {
					tmp[dfs(new Node(node.num-p[i],node.a+p[i],node.b,-1),step+1)]=1;
				}
				else {
					tmp[dfs(new Node(node.num-p[i],node.a,node.b+p[i],1),step+1)]=1;

				}
			}
		}
//		System.out.println("step:"+step+" 0:"+tmp[0]+" 1:"+tmp[1]+" 2:"+tmp[2]);
		if(tmp[2]==1)return 0;
		else if(tmp[1]==1) return 1;
		else return 2;
	}
	public static void main(String[] args)
	{
		Scanner sc=new Scanner(System.in);
		for(int i=0;i<3;i++){
			p[i]=sc.nextInt();
		}
		String s="";
		for(int i=0;i<5;i++){
			int num=sc.nextInt(),res;
			res=dfs(new Node(num,0,0,1),0);
			if(res==0) s+="+ ";
			else if(res==1) s+="0 ";
			else s+="- ";
//			System.out.println("***********");
		}
		System.out.println(s);
		
	}
}

由于我用Node来记录每次的情况,为了知道这轮谁取,就通过flag的值来判断,这也导致,在无法取球后,判断胜负的条件也是不一样的,所以要写两个不同情况的反馈。

也因为我用Node,我无法记录曾经出现过的情况,这回导致搜索时间变长,我会再修改。

贴一段学长的代码,用数组来记录的,但其中有些我还没看懂,还要再问。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int a, b, c;
int buf[1000][2][2];

int dfs(int n, int u, int v) {
	if (n < a && n < b && n < c) {
		if (u==v)
			return 1;
		else if (u)
			return 0;
		else
			return 2;
	}
	if (~buf[n][u][v])
		return buf[n][u][v];

	int f[3];
	f[0] = f[1] = f[2] = 0;
	if (n >= a)
		f[dfs(n-a, v, (u+a)&1)]++;
	if (n >= b)
		f[dfs(n-b, v, (u+b)&1)]++;
	if (n >= c)
		f[dfs(n-c, v, (u+c)&1)]++;
	if (f[2])
		buf[n][u][v] = 0;
	else if (f[1])
		buf[n][u][v] = 1;
	else
		buf[n][u][v] = 2;
	return buf[n][u][v];
}

int main(int argc, char const *argv[])
{
	scanf("%d%d%d", &a, &b, &c);
	memset(buf, -1, sizeof(buf));
	for (int i = 0; i < 5; i++) {
		int n;
		scanf("%d", &n);
		int ans = dfs(n, 0, 0);
		if (ans == 0)
			putchar('+');
		else if (ans == 1)
			putchar('0');
		else
			putchar('-');
		putchar(i==4?'\n':' ');
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值