如果说工作是为了更好地玩,那当玩的不开心时,比如24点,还是要用工作技能去处理,下面代码质量不高,纯属娱乐,欢迎指点批评:
public class Main {
private static final double TARGET=24;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] nums = Arrays.stream(in.nextLine().split(" ")).mapToInt(
Integer::parseInt).toArray();
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
Main m = new Main();
// 15<=>1111
System.out.println(m.dfs(nums, new ArrayList<>(), 15, nashorn));
}
/**
*
* @param nums
* @param formula list 存储表达式子项,之所有选用 List 是为了兼容数字大于9的情况
* @param selected 记录选择标志位
* @param nashorn
* @return
*/
public boolean dfs(int[] nums, List 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 formula, ScriptEngine nashorn) {
boolean ret = checkValue(calc(formula, nashorn) ,TARGET);
if (ret) {
return buildFormula(formula, null);
}
ArrayList 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 formulas, List> idxes) {
if (idxes == null) {
return formulas.stream().collect(Collectors.joining());
}
StringBuilder formula = new StringBuilder();
List ids = idxes.get(0);
if (idxes.size() == 1) {
List l1 = formulas.subList(0, ids.get(0));
List l2 = formulas.subList(ids.get(0), ids.get(1));
List 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 l1 = formulas.subList(ids.get(0), ids.get(1));
List 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 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 点规则,参考leetcode679.
牛客上也有相关题目 HJ67,只不过是用例不全的阉割版,用半成品算法也能通过,参考我这篇
靠着上述代码,总算通关了 24 点游戏 160+32 个关卡,姑且一乐,需者自取。
=====================补充=========================
优化添加 findOne 和 findAll:
public class Solve24Game{
private static final double TARGET = 24;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int[] nums = Arrays.stream(in.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
Solve24Game hj = new Solve24Game();
String r = hj.findOne24(nums,new ArrayList<>(),15,nashorn);
System.out.println("find one 24:" + (r.isEmpty()?"NONE":r));
Set resultSet = new HashSet<>();
hj.findAll24(nums, new ArrayList<>(), 15, nashorn, resultSet);
System.out.println("find all 24: ");
resultSet.forEach(System.out::println);
System.out.println(hj.solve24(nums, 24, 15));
System.out.println(new Solution().judgePoint24(nums));
}
/**
* @param nums 初始数
* @param formula list 存储表达式子项,之所有选用 List 是为了兼容数字大于9的情况
* @param selected 记录选择标志位
* @param nashorn 计算引擎
* @param resultSet 存放所有结果集
*/
public void findAll24(int[] nums, List formula, int selected, ScriptEngine nashorn, Set resultSet) {
if (selected == 0) {
if (formula.size() < 7) {
return;
}
// todo test
// System.out.println("complete formula: " + formula);
Set result = found24(formula, nashorn, false);
if (!result.isEmpty()) {
resultSet.addAll(result);
}
return;
}
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));
findAll24(nums, formula, newSelect, nashorn, resultSet);
formula.remove(formula.size() - 1);
continue;
}
if (!checkI(i, selected)) {
continue;
}
// addition
formula.add("+");
formula.add(String.valueOf(select));
findAll24(nums, formula, newSelect, nashorn, resultSet);
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
// subtraction
formula.add("-");
formula.add(String.valueOf(select));
findAll24(nums, formula, newSelect, nashorn, resultSet);
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
// multiplication
formula.add("*");
formula.add(String.valueOf(select));
findAll24(nums, formula, newSelect, nashorn, resultSet);
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
// division
formula.add("/");
formula.add(String.valueOf(select));
findAll24(nums, formula, newSelect, nashorn, resultSet);
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
}
}
/**
*
* @param nums
* @param formula list 存储表达式子项,之所有选用 List 是为了兼容数字大于9的情况
* @param selected 记录选择标志位
* @param nashorn
* @return
*/
public String findOne24(int[] nums, List formula, int selected, ScriptEngine nashorn) {
if (selected == 0) {
if (formula.size() < 7) {
return "";
}
// todo test
// System.out.println("complete formula: " + formula);
Set f = found24(formula, nashorn, true);
if (!f.isEmpty()) {
return f.stream().findFirst().get();
}else{
return "";
}
}
String ret = "";
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 = findOne24(nums, formula, newSelect, nashorn)).isEmpty()) {
formula.remove(formula.size() - 1);
continue;
}else{
return ret;
}
}
if (!checkI(i, selected)) {
continue;
}
// addition
formula.add("+");
formula.add(String.valueOf(select));
ret = findOne24(nums, formula, newSelect, nashorn);
if (ret.isEmpty()) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return ret;
}
// subtraction
formula.add("-");
formula.add(String.valueOf(select));
ret = findOne24(nums, formula, newSelect, nashorn);
if (ret.isEmpty()) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return ret;
}
// multiplication
formula.add("*");
formula.add(String.valueOf(select));
ret = findOne24(nums, formula, newSelect, nashorn);
if (ret.isEmpty()) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return ret;
}
// division
formula.add("/");
formula.add(String.valueOf(select));
ret = findOne24(nums, formula, newSelect, nashorn);
if (ret.isEmpty()) {
formula.remove(formula.size() - 1);
formula.remove(formula.size() - 1);
} else {
return ret;
}
}
return "";
}
/**
* 校验(从右到左)第 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
* @param findOne
* @return 可得 24 点的表达式
*/
public Set found24(List formula, ScriptEngine nashorn, boolean findOne) {
Set resultSet = new HashSet<>();
boolean found = checkValue(calc(formula, nashorn), TARGET);
if (found) {
resultSet.add(buildFormula(formula, null));
if (findOne) return resultSet;
}
ArrayList 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));
found = checkValue(calc(tempFormu, nashorn), TARGET);
if (found) {
resultSet.add(buildFormula(formula, Arrays.asList(Arrays.asList(start, end))));
if (findOne)return resultSet;
}
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)) + ")");
found = checkValue(calc(tempFormu, nashorn), TARGET);
tempFormu.clear();
if (found) {
resultSet.add(buildFormula(formula, Arrays.asList(Arrays.asList(0, 3), Arrays.asList(4, 7))));
if (findOne)return resultSet;
}
// (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));
found = checkValue(calc(tempFormu, nashorn), TARGET);
tempFormu.clear();
if (found) {
resultSet.add(buildFormula(formula, Arrays.asList(Arrays.asList(start, end))));
if (findOne)return resultSet;
}
}
return resultSet;
}
/**
* 校验值 误差 < 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 formulas, List> idxes) {
if (idxes == null) {
return formulas.stream().collect(Collectors.joining());
}
StringBuilder formula = new StringBuilder();
List ids = idxes.get(0);
if (idxes.size() == 1) {
List l1 = formulas.subList(0, ids.get(0));
List l2 = formulas.subList(ids.get(0), ids.get(1));
List 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 l1 = formulas.subList(ids.get(0), ids.get(1));
List 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 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;
}
}
}
效果图: