24点游戏:java的一种穷举实现

24点游戏:java的一种穷举实现

创号以来的第一篇博客,希望看到的各位都支持下哈。


24点纸牌游戏是我很喜欢的一个游戏,一直没有编程去实现过,百度百科就有提供的方法去实现这一算法,用到了中缀及后缀表达式。而我也在周一的时候自己尝试实现了这个程序:这里提供的是一种穷举法的思路。


实现是用的java,首先还是先说一下规则吧:
  有四种花色的扑克牌,大小都是从1到10,现在从中选取4张牌,计算这四张牌的算术组合结果能否等于24.
  例如这么一组数:
5 5 5 1
对于这一组数,如何让它构成24点呢?答案是:5 × (5 - (1 ÷ 5)) = 24 。 
  
  
题目思路:
一、一般思路:
这道题的思路首先想到的肯定是穷举,那么究竟会有哪些情况呢?

没有括号的情况:
一共4个数,4的情况下一共4个数,4! = 24 种情况;三个运算符,每个运算符只可能是(+ - x ÷)中的一种,4^3 = 64 种情况。


有括号的情况:
1、形如 (a * b) * c * d);
2、形如 (a * b * c) * d);
3、形如 a * (b * c) * d);
4、形如 (a * b) * (c * d));
5、形如 ((a * b) * c) * d);
6、形如 (a * (b * c)) * d);

对于上述思路来说,实现的话就是穷举出来所有数,所有运算符以及所有括号可能的情况,然后一一计算判断。这样的话情况应该也不会特别多,至少是计算机可以接受的范围。但是括号的形式太多了,穷举之后一定要通过 递归+回溯 来实现所有可能情况的判断,对于上述情况来说,可能的情况太多,也许在编码的时候自己就会凌乱了,所以虽说要穷举,但是也要对于穷举的方式给予一定的修改。


二、穷举整改后的思路:

假设我们用⊙表示运算,⊙除了可以表示基本的 "+", "-", "x", "÷"外。我们还引入两个新的运算,"反-",和"反÷"。


比如(a 反÷ b)的意思是(b ÷ a)。则对形如(c ÷ (a + b))的形式,就可以等价的描述为((a + b) 反/ c)。
这样描述可以减少算式中括号的情况。
利用这6种运算,可以将所有可能的计算过程归结为2类:
(((a ⊙ b) ⊙ c ) ⊙ d)
((a ⊙ b) ⊙ (c ⊙ d))

将4张牌的值,分别代入a,b,c,d,再把可能的运算符也代入。就可以得到相应的计算式子,将其计算出来,再检查结果是否等于24。

由于有4个数,所以对于a,b,c,d的对应关系有 4!=24 种情况。3个运算符,每个运算符可能有6种情况,那就是 6^3 = 216。再考虑到2种不同的模式,所以一共有2 * 24 * 216 = 10368种情况。


在这么多中情况中肯定会有重复的情况,所以在此基础上可以对其进行剪枝,但是剪起来会相当麻烦,所以这里就不再剪了,一万种情况对于计算机来说还是可以满足的。私下里可以再尝试对这种算法的剪枝。所以到这一步,就可以代码实现了:


下面附上我的代码:
import java.util.Scanner;

public class Point24 {

	boolean used[] = { false, false, false, false };
	float nowNumber[] = { 0, 0, 0, 0 };
	float number[] = { 0, 0, 0, 0 };
	int[] ops = { 0, 0, 0 };
	int opType[] = { 1, 2, 3, 4, 5, 6 };

	public void getInput(Scanner scan) {
		for (int i = 0; i < 4; i++) {
			number[i] = scan.nextFloat();
		}
	}

	public boolean makeNumber(int depth) {
		if (depth >= 4) {
			// 此时已经枚举完四个数了,
			// 开始枚举运算符
			return makeOperation(0);
		}

		for (int i = 0; i < 4; i++) {
			if (!used[i]) {
				nowNumber[depth] = number[i];
				used[i] = true;
				if (makeNumber(depth + 1)) {
					return true;
				}
				used[i] = false;
			}
		}

		return false;
	}

	public boolean makeOperation(int depth) {
		if (depth >= 3) {
			// 此时已经枚举了四个数和三个运算符
			// 计算第一种模式下的值
			if (calcType1() == 24.0) {
				return true;
			}
			if (calcType2() == 24.0) {
				return true;
			}
			return false;
		}

		for (int i = 0; i < 6; i++) {
			ops[depth] = opType[i];
			if (makeOperation(depth + 1))
				return true;
		}

		return false;
	}

	public float calculate(float a, float b, int type) {

		switch (type) {
		case 1:
			return (a + b);
		case 2:
			return (a - b);
		case 3:
			return (a * b);
		case 4:
			if (b == 0)
				return 0;
			return (a / b);
		case 5:
			return (b - a);
		case 6:
			if (a == 0)
				return 0;
			return (b / a);
		}

		return 0;
	}

	public float calcType1() {
		float result = nowNumber[0];

		result = calculate(result, nowNumber[1], ops[0]);
		result = calculate(result, nowNumber[2], ops[1]);
		result = calculate(result, nowNumber[3], ops[2]);

		return result;
	}

	public float calcType2() {
		float result1 = nowNumber[0];
		float result2 = nowNumber[2];

		result1 = calculate(result1, nowNumber[1], ops[0]);
		result2 = calculate(result2, nowNumber[3], ops[2]);
		result1 = calculate(result1, result2, ops[1]);

		return result1;
	}

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		int n = scan.nextInt();
		String result[] = new String[n];
		Point24 point;
		for (int i = 0; i < n; i++) {
			point = new Point24();
			point.getInput(scan);
			result[i] = point.makeNumber(0) ? "Yes" : "No";
		}

		for (int i = 0; i < n; i++) {
			System.out.println(result[i]);
		}
	}

}




 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值