24点游戏:java的一种穷举实现
创号以来的第一篇博客,希望看到的各位都支持下哈。
实现是用的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]);
}
}
}