题目介绍
24点游戏是经典的纸牌益智游戏。
常见游戏规则:
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。(其中,J代表11,Q代表12,K代表13,A代表1),按照要求编程解决24点游戏。
基本要求: 随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式,用擅长的语言(C/C++/Java或其他均可)实现程序解决问题。
思路分析
本题主要解决4个数字的全排列后的运算结果,重点在于输出四个数字的全排列,以及对于五种运算方法的穷举,分别是(\*代表任意运算符):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)
符号位置固定,只需通过迭代,遍历出所有的符号组合即可。
核心算法
1.四个数字的全排列遍历
利用递归算法,遍历四个数字的全排列
代码如下(示例):
/**
* @return void
* @params []
* @author Wang Rongqiang
* @Description //TODO 获取纸牌全排列
* @date 2021/5/9 18:45
*/
public void getArrange(int n) {
if (n == num.length) {
// 得到一种排列组合,对齐进行操作
// insertSymbol();
} else {
for (int i = n; i < num.length; i++) {
swap(i, n);
getArrange(n + 1);
swap(i, n);
}
}
}
/**
* @return void
* @params [i, n]
* @author Wang Rongqiang
* @Description //TODO 交换数组num中下标为i、n的元素位置
* @date 2021/5/9 18:57
*/
private void swap(int i, int n) {
int tmp = num[i];
num[i] = num[n];
num[n] = tmp;
}
2.构造计算式并验证结果
代码如下(示例):
/**
* @return void
* @params []
* @author Wang Rongqiang
* @Description //TODO 在不同的位置插入不同运算符
* @date 2021/5/9 19:03
*/
private void insertSymbol() {
char[] oper = new char[3];
int i, j, k;
for (i = 0; i < 4; i++) { //对4种运算符的3个位置排列
oper[0] = symbol[i];
for (j = 0; j < 4; j++) {
oper[1] = symbol[j];
for (k = 0; k < 4; k++) {
oper[2] = symbol[k];
// 构建运算表达式
getResult(oper);
}
}
}
}
/**
* @return void
* @params [oper]
* @author Wang Rongqiang
* @Description //TODO 根据运算符号集构建计算式并验证
* @date 2021/5/9 19:16
*/
private void getResult(char[] oper) {
// 记录运算结果
double t;
//穷举运算次序
//1.((A*B)*C)*D
t = 0;
t = getValue(num[0], num[1], oper[0]);
t = getValue(t, num[2], oper[1]);
t = getValue(t, num[3], oper[2]);
// 验证运算结果
if (Math.abs(t - 24) < 0.0001) {
result.add("((" + card[num[0] - 1] + " " + oper[0] + " " + card[num[1] - 1] + ") "
+ oper[1] + " " + card[num[2] - 1] + ") " + oper[2] + " " + card[num[3] - 1] + " = 24");
return;
}
//2.(A*(B*C))*D
t = 0;
t = getValue(num[1], num[2], oper[1]);
t = getValue(num[0], t, oper[0]);
t = getValue(t, num[3], oper[2]);
// 验证运算结果
if (Math.abs(t - 24) < 0.0001) {
result.add("(" + card[num[0] - 1] + " " + oper[0] + " (" + card[num[1] - 1] + " " +
oper[1] + " " + card[num[2] - 1] + ")) " + oper[2] + " " + card[num[3] - 1] + " = 24");
return;
}
//3.(A*B)*(C*D)
t = 0;
t = getValue(getValue(num[0], num[1], oper[0]), getValue(num[2], num[3], oper[2]), oper[1]);
// 验证运算结果
if (Math.abs(t - 24) < 0.0001) {
result.add("(" + card[num[0] - 1] + " " + oper[0] + " " + card[num[1] - 1] + ") " +
oper[1] + " (" + card[num[2] - 1] + " " + oper[2] + " " + card[num[3] - 1] + ") = 24");
return;
}
//4.A*(B*(C*D))
t = 0;
t = getValue(num[2], num[3], oper[2]);
t = getValue(num[1], t, oper[1]);
t = getValue(num[0], t, oper[0]);
// 验证运算结果
if (Math.abs(t - 24) < 0.0001) {
result.add(card[num[0] - 1] + " " + oper[0] + " (" + card[num[1] - 1] + " " +
oper[1] + " (" + card[num[2] - 1] + " " + oper[2] + " " + card[num[3] - 1] + ")) = 24");
return;
}
//5.A*((B*C)*D)
t = 0;
t = getValue(num[1], num[2], oper[1]);
t = getValue(t, num[3], oper[2]);
t = getValue(num[0], t, oper[0]);
// 验证运算结果
if (Math.abs(t - 24) < 0.0001) {
result.add(card[num[0] - 1] + " " + oper[0] + " ((" + card[num[1] - 1] + " " +
oper[1] + " " + card[num[2] - 1] + ") " + oper[2] + " " + card[num[3] - 1] + ") = 24");
}
}
/**
* @return double
* @params [a, b, symb]
* @author Wang Rongqiang
* @Description //TODO 计算a b两数根据symb符号进行运算的结果
* @date 2021/5/10 19:40
*/
private double getValue(double a, double b, char symb) {
double result = 0;
switch (symb) {
case '+':
result = a + b;
break;
case '-':
result = a - b;
break;
case '*':
result = a * b;
break;
case '/':
result = a / b;
break;
default:
break;
}
return result;
}
总结
此次程序设计方法学的作业,让我学到了很多,让我加深了对穷举法的认识,训练了我运用穷举法解决实际问题的能力。同时让我对java的面向对象的编程思想又有了更深层的理解。我将会继续将这种思想运用在自己的程序设计中,强化自己的技能。