[leetCode]679. 24 点游戏
回溯法
一共4个数字4种运算,可以通过回溯法遍历不同的可能性。
- 从4个数字中取出两个数字,并选择一种运算有 4*3=12种,将运算结果取代取出的两个数字等到3个数字
- 在剩下三个数字种取出两个数字并选择一种运算有 3*2 = 6种, 将运算结果取代取出的两个数字还剩2数字
- 剩下两个数字有两种不同的顺序,可选择一种运算
因此,一共有:124+64+2*4=9216种可能性。
回溯的具体做法是,使用一个列表存储目前的全部数字,每次从列表中选出 2 个数字,再选择一种运算操作,用计算得到的结果取代选出的 2 个数字,这样列表中的数字就减少了 1 个。重复上述步骤,直到列表中只剩下 1个数字,这个数字就是一种可能性的结果,如果结果等于 24,则说明可以通过运算得到 24。如果所有的可能性的结果都不等于 24,则说明无法通过运算得到 24
注意点:
- 需要注意浮点运算的精度与除数为0的情况
- 由于加法与乘法符合交换律因此可以进行优化,跳过一种顺序
class Solution {
// 目标值
static final int TARGET = 24;
// 浮点运算允许的误差
static final double EPSILON = 1e-6;
// 加减乘除
static final int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;
public boolean judgePoint24(int[] nums) {
// 将四个数字加入链表
List<Double> list = new ArrayList<>();
for (int num : nums)
list.add((double)num);
return solve(list);
}
private boolean solve(List<Double> list) {
if (list.size() == 0) return false;
if (list.size() == 1)
return Math.abs(list.get(0) - TARGET) < EPSILON;
int size = list.size();
// i,j为取出两个数字的下标
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++) {
if (i != j) {
List<Double> list2 = new ArrayList<>();
// 添加 i, j以外的另外两个数字
for (int k = 0; k < size; k++) {
if (k != i && k != j) {
list2.add(list.get(k));
}
}
for (int k = 0; k < 4; k++) {
// 加法和乘法符合交换律,对于取出的两个数字其中一种顺序不用考虑
if (k < 2 && i > j)
continue;
if (k == ADD) {
list2.add(list.get(i) + list.get(j));
} else if (k == MULTIPLY) {
list2.add(list.get(i) * list.get(j));
} else if (k == SUBTRACT) {
list2.add(list.get(i) - list.get(j));
}else if (k == DIVIDE) {
if (Math.abs(list.get(j)) < EPSILON)
continue;
else
list2.add(list.get(i) / list.get(j));
}
if (solve(list2))
return true;
list2.remove(list2.size() - 1);
}
}
}
return false;
}
}