24点游戏解法
本文提供了 24 点游戏的解法,不只包括是否满足 24 点规则。
限首先官方给的案例是不全的,先上一段可以 100% 通过的 bug:
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] nums = Arrays.stream(in.nextLine().split(" ")).mapToInt(
Integer::parseInt).toArray();
System.out.println(solve24(nums, 24, 15));
}
// selected:二进制去重,15 即为 1111
public static boolean solve24(int[] nums, double target, int selected) {
if (selected == 0) {
return target == 0.00d;
}
for (int i = 0; i < nums.length; ++i) {
if (!checkI(i, selected)) {
continue;
}
int newSelected = setI(i, selected);
int select = nums[i];
boolean result = false;
result |= solve24(nums, target + select, newSelected);
result |= solve24(nums, target - select, newSelected);
result |= solve24(nums, target * select, newSelected);
result |= solve24(nums, target / select, newSelected);
if (result) {
return true;
}
}
return false;
}
// 校验 i 为是否为1
public static boolean checkI(int i, int selected) {
return ((selected >> i) & 1) == 1;
}
// 第 i 为置为0
public static int setI(int i, int selected) {
int c = ~(1 << i);
return selected & c;
}
上述代码的核心思想为:
既然数字可以组合得到24点,那么用 24 做逆向运算,如果最终结果为0,则表示满足 24 点规则。
而问题出在:
忽略了括号相关的组合情况,例如:1 9 1 2,显然应该是 (9-1)*(1+2),此方案不能解答,而官网给的用例却全部通过了(手动狗头~~)
具体解法可以参照 leetcode 679 。
实不相瞒,代码写完就去玩 24 点游戏了,有的题解不出来,很是头疼,索性写了个解法:
private static final double TARGET=24;
/**
*
* @param nums
* @param formula list 存储表达式子项,之所有选用 List<String> 是为了兼容数字大于9的情况
* @param selected 记录选择标志位
* @param nashorn
* @return
*/
public boolean dfs(int[] nums, List<String> formula, int selected, ScriptEngine nashorn) {
if (selected == 0) {
if (formula.size() < 7) {
return false;
}
// todo test
// System.out.println("complete formula: " + formula);
String f = found24(formula, nashorn);
boolean b = !f.isEmpty();
if (b) {
System.out.println(f);
}
return b;
}
boolean ret = false;
for (int i = 0; i < nums.length; ++i) {
int select = nums[i];
int newSelect = setI(i, selected);
if (selected == 15) {//1111
formula.add(String.valueOf(select));
if (!(ret |= dfs(nums, formula, newSelect, nashorn))) {
formula.remove(formula.size() - 1);
continue;
}else{
return true;
}
}
if (!checkI(i, selected)) {
continue;
}
// addition
formula.add("+");
formula.add(String.valueOf(select));
ret |= dfs(nums, formula, newSelect, nashorn);
if (!ret) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return true;
}
// subtraction
formula.add("-");
formula.add(String.valueOf(select));
ret |= dfs(nums, formula, newSelect, nashorn);
if (!ret) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return true;
}
// multiplication
formula.add("*");
formula.add(String.valueOf(select));
ret |= dfs(nums, formula, newSelect, nashorn);
if (!ret) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return true;
}
// division
formula.add("/");
formula.add(String.valueOf(select));
ret |= dfs(nums, formula, newSelect, nashorn);
if (!ret) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return true;
}
}
return false;
}
/**
* 校验(从右到左)第 i 位是否为1
* @param i
* @param selected
* @return
*/
public boolean checkI(int i, int selected) {
return ((selected >> i) & 1) == 1;
}
/**
* 从右到左 第 i 位置为0
* @param i
* @param selected
* @return
*/
public int setI(int i, int selected) {
int c = ~(1 << i);
return selected & c;
}
/**
* 枚举各种括号,找出带括号计算后可以得到 24 点的表达式
* @param formula 包含表达式符号的集合
* @param nashorn
* @return 可得 24 点的表达式
*/
public String found24(List<String> formula, ScriptEngine nashorn) {
boolean ret = checkValue(calc(formula, nashorn) ,TARGET);
if (ret) {
return buildFormula(formula, null);
}
ArrayList<String> tempFormu = new ArrayList<>();
// (AB)CD A(BC)D AB(CD)
for (int i = 0; i < 3; ++i) {
int end = 3 + i * 2;
int start = end - 3;
// 拼接表达式,一下类似,不重复注释
tempFormu.addAll(formula.subList(0, start));
tempFormu.add("("+String.valueOf(calc(formula.subList(start, end), nashorn)) + ")");
tempFormu.addAll(formula.subList(end, 7));
ret = checkValue(calc(tempFormu,nashorn),TARGET);
if (ret) {
return buildFormula(formula, Arrays.asList(Arrays.asList(start, end)));
}
tempFormu.clear();
}
// (AB)(CD)
tempFormu.add("("+String.valueOf(calc(formula.subList(0, 3), nashorn)) + ")");
tempFormu.addAll(formula.subList(3, 4));
tempFormu.add("("+String.valueOf(calc(formula.subList(4, 7), nashorn))+")");
ret = checkValue(calc(tempFormu,nashorn),TARGET);
tempFormu.clear();
if (ret) {
return buildFormula(formula, Arrays.asList(Arrays.asList(0, 3), Arrays.asList(4, 7)));
}
// (ABC)D A(BCD)
for (int i = 0; i < 2; ++i) {
int end = 5 + i * 2;
int start = end - 5;
tempFormu.addAll(formula.subList(0, start));
tempFormu.add("("+String.valueOf(calc(formula.subList(start, end), nashorn))+")");
tempFormu.addAll(formula.subList(end, 7));
ret = checkValue(calc(tempFormu,nashorn),TARGET);
tempFormu.clear();
if (ret) {
return buildFormula(formula, Arrays.asList(Arrays.asList(start, end)));
}
}
return "";
}
/**
* 校验值 误差 < 1e-6
* @param value
* @param target
* @return
*/
public boolean checkValue(double value, double target){
return Math.abs(target-value)<1e-6;
}
/**
* 构建公式的字符串表达式(含有符号)
* @param formulas
* @param idxes 插入括号的下标(两层为了兼容双括号 (AB)(CD))
* @return
*/
public String buildFormula(List<String> formulas, List<List<Integer>> idxes) {
if (idxes == null) {
return formulas.stream().collect(Collectors.joining());
}
StringBuilder formula = new StringBuilder();
List<Integer> ids = idxes.get(0);
if (idxes.size() == 1) {
List<String> l1 = formulas.subList(0, ids.get(0));
List<String> l2 = formulas.subList(ids.get(0), ids.get(1));
List<String> l3 = formulas.subList(ids.get(1), 7);
formula.append(l1.stream().collect(Collectors.joining()));
formula.append("(" + l2.stream().collect(Collectors.joining()) + ")");
formula.append(l3.stream().collect(Collectors.joining()));
} else {//双括号只有一种可能
List<String> l1 = formulas.subList(ids.get(0), ids.get(1));
List<String> l2 = formulas.subList(idxes.get(1).get(0), idxes.get(1).get(1));
formula.append("(" + l1.stream().collect(Collectors.joining()) + ")");
formula.append(formulas.get(3));
formula.append("(" + l2.stream().collect(Collectors.joining()) + ")");
}
return formula.toString();
}
public double calc(List<String> formula0, ScriptEngine nashorn) {
try {
String formula = formula0.stream().collect(Collectors.joining());
// todo Test
// System.out.println("calculating : " + formula);
Object result = nashorn.eval(formula);
if (result instanceof Integer) {
return (Integer) result;
}
if (result instanceof Double) {
double d = (Double) result;
return d;
}
return 0;
} catch (ScriptException e) {
return 0;
}
}
靠着上述代码,总算通关了 24 游戏 160+32 个关卡,姑且一乐,需者自取
=======================补充==========================
关于24点游戏详细完整解法,可以参考这篇博客:https://blog.csdn.net/weixin_41346635/article/details/134467657
#24点#
