24点游戏
##方法一:回溯
可以通过回溯的方法遍历所有不同的可能性。具体做法是,使用一个列表存储目前的全部数字,每次从列表中选出 2 个数字,再选择一种运算操作,用计算得到的结果取代选出的 2 个数字,这样列表中的数字就减少了 1 个。重复上述步骤,直到列表中只剩下 1 个数字,这个数字就是一种可能性的结果,如果结果等于 24,则说明可以通过运算得到 24。如果某一种可能无法得到等于24的结果,那么就进行回溯,具体做法是把之前计算得到的结果从列表中移除,然后进行下一轮搜索。如果所有的可能性的结果都不等于 24,则说明无法通过运算得到 24。
实现时,有一些细节需要注意。
除法运算为实数除法,因此结果为浮点数,列表中存储的数字也都是浮点数。在判断结果是否等于 2424 时应考虑精度误差,这道题中,误差小于 $10^{-6}$可以认为是相等。
进行除法运算时,除数不能为 0,如果遇到除数为 0 的情况,则这种可能性可以直接排除。由于列表中存储的数字是浮点数,因此判断除数是否为 0 时应考虑精度误差,这道题中,当一个数字的绝对值小于 $10^{-6}$时,可以认为该数字等于 0。
还有一个可以优化的点。加法和乘法都满足交换律,因此如果选择的运算操作是加法或乘法,则对于选出的 2 个数字不需要考虑不同的顺序,在遇到第二种顺序时可以不进行运算,直接跳过。
1 | class Solution { |
2 | public static final double EPSILON = 1e-6; |
3 | public static final int ADD = 0; |
4 | public static final int MUL = 1; |
5 | public static final int SUB = 2; |
6 | public static final int DIV = 3; |
7 | public static final int TARGET = 24; |
8 | public boolean judgePoint24(int[] nums) { |
9 | List<Double> list = new ArrayList<Double>(); |
10 | for(int num : nums){ |
11 | list.add((double)num); |
12 | } |
13 | return solve(list); |
14 | } |
15 | |
16 | public boolean solve(List<Double> list){ |
17 | if(list.size() == 0){ |
18 | return false; |
19 | } |
20 | if(list.size() == 1){ |
21 | //判断是否满足24点 |
22 | return Math.abs(list.get(0)-TARGET)<EPSILON; |
23 | } |
24 | int size = list.size(); |
25 | for(int i=0; i<size; i++){ |
26 | for(int j=0; j<size; j++){ |
27 | if(i != j){ |
28 | List<Double> list2 = new ArrayList<Double>(); |
29 | for(int k=0; k<size; k++){ |
30 | if(k!=i && k!=j){ |
31 | list2.add(list.get(k)); |
32 | } |
33 | } |
34 | for(int k=0; k<4; k++){ |
35 | //加法和乘法满足交换律 |
36 | if(i>j && k<2){ |
37 | continue; |
38 | } |
39 | double num1 = list.get(i); |
40 | double num2 = list.get(j); |
41 | if(k == 0){ |
42 | list2.add(num1 + num2); |
43 | }else if(k == 1){ |
44 | list2.add(num1 * num2); |
45 | }else if(k == 2){ |
46 | list2.add(num1 - num2); |
47 | }else if(k == 3){ |
48 | if(num2 < EPSILON){ |
49 | continue; |
50 | }else{ |
51 | list2.add(num1 / num2); |
52 | } |
53 | } |
54 | if(solve(list2)){ |
55 | return true; |
56 | } |
57 | //回溯,进入另一个循环继续搜索 |
58 | list2.remove(list2.size()-1); |
59 | } |
60 | } |
61 | } |
62 | } |
63 | return false; |
64 | } |
65 | } |
复杂度分析:
- 时间复杂度:$O(1)$
- 空间复杂度:$O(1)$