24 点游戏

156 阅读1分钟

1、题目描述

你有 4 张写有 1 到 9 数字的牌。你需要判断是否能通过 */+-() 的运算得到 24。注意事项如下。

  1. 除法运算符 / 表示实数除法,而不是整数除法。例如 4 / (1 - 2/3) = 12 。
  2. 每个运算符对两个数进行运算。特别是我们不能用 - 作为一元运算符。例如,[1, 1, 1, 1] 作为输入时,表达式 -1 - 1 - 1 - 1 是不允许的。
  3. 你不能将数字连接在一起。例如,输入为 [1, 2, 1, 2] 时,不能写成 12 + 12 。 示例 1:
输入: [4, 1, 8, 7]
输出: True
解释: (8-4) * (7-1) = 24

2、思路

卡牌的数量只有四张,所以我们可以将卡牌进行全排列,一共有 4 * 3 * 2 * 1 = 24 种排列组合情况。而后再用递归的方法进行一个运算符运算,以此合并两个数成一个数,缩小数据个数(要考虑所有的两个不等价数的运算情况)。最后缩小成一个数时,进行判断即可,我们使用的是小数代替分数,因此在最后的结果会有很小的误差,所以在接近 24 的值就是可以组合了。

3、代码及注解

public boolean judgePoint24(int[] c) {
        double[] cards = new double[4];
        for (int i = 0; i < 4; i++) {
            cards[i] = c[i];
        }
        for (int i = 0; i < 4; i++) { // 对卡牌进行全排列
            for (int j = 0; j < 4; j++) {
                if (i == j) continue;
                for (int m = 0; m < 4; m++) {
                    if (i == m || j == m) continue;
                    for (int n = 0; n < 4; n++) {
                        if (i == n || j == n || m == n) continue;
                        if (check_4(cards[i],cards[j],cards[m],cards[n])) return true;
                    }
                }
            }
        }
        return false;
    }
    // 缩小一个运算符,因为 d1 和 d2 和 d3 和 d4 是等价的,所以考虑前两个就可以了。
    private boolean check_4(double d1, double d2, double d3, double d4) {
        return check_3(d1 + d2,d3,d4) ||  
               check_3(d1 - d2,d3,d4) ||
               check_3(d1 * d2,d3,d4) ||
               check_3(d1 / d2,d3,d4);
    }
    // 缩小一个运算符,d2 和 d3 是等价的,d1 与 d2 不等价,d1 与 d3 不等价,要对 d1 与 d2、d1 与 d3 进行交换递归。
    private boolean check_3(double d1, double d2, double d3) {
        return check_2(d1 + d2,d3) ||
               check_2(d1 - d2,d3) ||
               check_2(d1 * d2,d3) ||
               check_2(d1 / d2,d3) ||
               check_2(d2 - d1,d3) ||
               check_2(d2 / d1,d3) ||
               check_2(d1,d2 + d3) ||
               check_2(d1,d2 - d3) ||
               check_2(d1,d2 * d3) ||
               check_2(d1,d2 / d3);
    }
    // 此时 d1 与 d2 全都不等价,所以要考虑d1 d2 的全部组合运算情况。
    private boolean check_2(double d1, double d2) {
        return check(d1 + d2) ||
               check(d1 - d2) ||
               check(d1 * d2) ||
               check(d1 / d2) ||
               check(d2 - d1) ||
               check(d2 / d1);
    }
    // 由于我们用切确值表示分数,所以最后计算结果接近估计值就可以了。
    private boolean check(double d) {
        return d > 23.9 && d < 24.1;
    }